3

Perhaps the question is more comprehensive than the title. There are a lot of posts about Authentication, tokens, JWT, Azure AD, etc. but all these posts seem to tell a different story which make the basic concept, at least to me, a bit unclear. I'll try to explain my questions using my case.

I have build a Frontend application with React in Visual code and a Backend application in Visual Studio .Net Core 2 (Web API). The application are being hosted in Azure and we would like to do authentication with Azure AD.

The frontend uses Adal-React (https://learn.microsoft.com/en-us/azure/active-directory/develop/active-directory-authentication-libraries) and Axios (https://github.com/axios/axios).
With the help of tutorials I have set up the configuration in the Frontend and in the API (backend), using namely the TenantID and ClientID which are listed in the Azure environment.

Although I have set it up and it is working, the way how it works is more or less unclear to me.

I'll try to explain the current workflow in my applications as it now is and listed my current questions below:

1 - The user navigates to the frontend application, adal checks if the user is authenticated and if it is not the person is redirected to our azure login environment, this is set up in the adal config (frontend) as follows:

  const adalConfig = {
         tenant: 'vhsprod.onmicrosoft.com',
         clientId: 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx',
         endpoints: {
          api: 'https://xxxxx.onmicrosoft.com/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx (client id)',
         },
         postLogoutRedirectUri: window.location.origin,
         redirectUri: 'http://localhost:3000/user-form',
         cacheLocation: 'sessionStorage'
        };
        export const authContext = new AuthenticationContext(adalConfig);
        export const getToken = () => {
         return authContext.getCachedToken(authContext.config.clientId);
        };

2 - the user needs to login and is being checked if the user exists in the Azure AD environment, if so, then the user gets redirected back to the frontend and we got our self a token.

3 - The user opens a form / page in the frontend which requires data from the backend, a API call is being made with the just received token. The call is being done using Axios in the frontend, Axios is also configured:

export const axiosCallToMyAPI = axios.create({
  baseURL: 'http://localhost:52860/api/',
  timeout: 5000,
  headers: {'Authorization': 'Bearer ' + initialToken}
});

The initial token is being received from Adal, the token we just received:

let initialToken = sessionStorage.getItem('adal.idtoken')

4 - The call is being made to the API and here we have also set up the configuration (appsettings.json) for using Azure AD:

"AzureAd": {
    "Instance": "https://login.microsoftonline.com/",
    "TenantDomain": "xxxx.onmicrosoft.com",
    "TenantId": "xxxx.onmicrosoft.com",
    "ClientId": "xxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxx"
  }

5 - In the startup class of the API the token is being validated:

services
                .AddAuthentication(sharedOptions =>
                {
                    sharedOptions.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
                    sharedOptions.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme; 
                    sharedOptions.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme; 
                })
                .AddJwtBearer(options =>
                {
                    options.Audience = this.Configuration["AzureAd:ClientId"];
                    options.Authority = $"{this.Configuration["AzureAd:Instance"]}{this.Configuration["AzureAd:TenantId"]}";

                    options.Events = new JwtBearerEvents()
                    {

                        OnTokenValidated = context =>
                        {
                            return Task.CompletedTask;
                        }
                    };
                });

The questions that remain and the assumptions made by me (reading several posts) are the following:

  • With the Azure token, which the frontend receives, a call to the API is being made, how does the API knows this token is 'valid'? I know the startup class of the API uses the event OnTokenValidated but what is the magic behind this? Is it purely checking the configured Tenant / Client ID in the appsettings with the token that is being received??
  • In several posts they use/mention a SecretKey / Signing Key, in my example however I have not implemented the SecretKey, yet it still works. Do I need to implement the secret / signing key in my API (backend)? Is it necessary?
  • I have also read up on Implicit flow, OpenID connect and some other terms. When using Azure AD authentication like in my example, and doing it this way, am I then automatically doing implicit flow (frontend --> backend) and authentication with openid connect? In other words, when using Azure authentication, are you then automatically doing these / the best practices, or should you still implement it?
  • The token has certain claims /. scopes defined which are being supplied by Azure (where you can also set these claims like email and roles), however when using AspNet (Identity) you can also add your own claims to the current token / session, for example when I add a roleclaim from aspnetidentity to my token. How do these claims differ from the original claim (still bearer / jwt?) and is this a good practice?

Could anyone confirm / explain these questions?

My apologies if the story is a bit to general, but at the moment our trial-error rate is way to high and I just am trying to clear things up.

1 Answer 1

2

With the Azure token, which the frontend receives, a call to the API is being made, how does the API knows this token is 'valid'? I know the startup class of the API uses the event OnTokenValidated but what is the magic behind this? Is it purely checking the configured Tenant / Client ID in the appsettings with the token that is being received??

You specified the Authority when configuring authentication. The authentication handler downloads the OpenId Connect metadata from a URL like this when the app starts up: https://login.microsoftonline.com/joonasapps.onmicrosoft.com/.well-known/openid-configuration.

From there it acquires the public signing keys and valid issuer URI.

The handler checks that the token's digital signature is valid against the signing keys it got, that the issuer is valid, that the audience is valid, and that the token is not expired.

In several posts they use/mention a SecretKey / Signing Key, in my example however I have not implemented the SecretKey, yet it still works. Do I need to implement the secret / signing key in my API (backend)? Is it necessary?

Check above.

I have also read up on Implicit flow, OpenID connect and some other terms. When using Azure AD authentication like in my example, and doing it this way, am I then automatically doing implicit flow (frontend --> backend) and authentication with openid connect? In other words, when using Azure authentication, are you then automatically doing these / the best practices, or should you still implement it?

Implicit flow means that a client can get an access token directly from the authorization endpoint instead of asking for it from the token endpoint. Single Page Apps use it with hidden iframes to get access tokens and refresh expiring tokens. It does depend on the user's session with Azure AD remaining active. If you have a more "classical" back-end app, you'd probably use the authorization code flow instead of the implicit flow.

The token has certain claims /. scopes defined which are being supplied by Azure (where you can also set these claims like email and roles), however when using AspNet (Identity) you can also add your own claims to the current token / session, for example when I add a roleclaim from aspnetidentity to my token. How do these claims differ from the original claim (still bearer / jwt?) and is this a good practice?

Your app cannot modify the Azure AD token, period. Its signature would no longer be valid if you modified it. Instead, ASP.NET Core Identity uses a cookie + session to store the claims for the signed-in user. So the resulting user session will contain the claims from the token as well as the claims you have in your user store.

Sign up to request clarification or add additional context in comments.

4 Comments

Regarding the question about what flow is being used, it cannot be said that Azure AD (AAD) always uses a specific flow by default. But @juunas is correct in his statements about the implicit flow. To verify which flow is being used and whether you're receiving, then check the following parameters in the URL request that's sent to AAD, which are set by ADAL: 1. scope: For OIDC, this must have "openid". 2. response_type: For OIDC, must include "id_token". If "code" is present, then you also receive an OAuth bearer token. Ref: preview.tinyurl.com/yaqatsua
@juunas Great post! This certainly clears things up. Thank you for taking the time to elaborate on my questions!
The flow is basically decided by the response_type sent to the authorization endpoint. If it is set to code, then it uses authorization code flow. If token or id_token then it uses implicit flow. If combination of both, then hybrid flow :) Then there's also the backend flows...

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.