简体   繁体   中英

How to validate id_token received by asp.net mvc OWIN from azure AD B2C

We have a security requirement that we must validate the id token we receive from Azure AD B2C. We need to validate these at the minimum

customSecurityLevel , audience , not before and "expiration time", issuer, nonce

Looking asp.net MVC OWIN middleware, I noticed that that OpenIdConnectAuthenicationOptions provides these:

return new OpenIdConnectAuthenticationOptions
            {
                ...
                Notifications = new OpenIdConnectAuthenticationNotifications //Specifies events which the OpenIdConnectAuthenticationMiddleware invokes to enable developer control over the authentication process.
                {
                    AuthenticationFailed = this.AuthenticationFailed,
                    RedirectToIdentityProvider = this.RedirectToIdentityProvider,
                    AuthorizationCodeReceived = this.OnAuthorizationCodeReceived,
                },

                TokenValidationParameters = new TokenValidationParameters
                {
                    SaveSigninToken = true,           // Important to save the token in boostrapcontext
                    ValidateAudience = true,          // Validate the Audience
                    ValidateIssuer = true,            // Validate the Issuer
                    ValidateLifetime = true,          // Validate the tokens lifetime
                    ValidIssuer = Issuer,             // The issuer to be validated
                    ValidAudience = ClientId,          // The Audience to be validated
                },
            };

Being quiet new to OWIN, I'm trying to understand below:

  1. Does OWIN middleware magically validates the token we receive from Azure AD B2C or do we need to manually perform validation per this: https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-webapi-manual-jwt-validation/

  2. At what point in time should token validation should occur ie AuthorizationCodeReceived event or on redirect controller/action (page) that's configured on Azure AD B2C redirect URL?

  3. We need to validate more attributes that TokenValidationParameters supports eg customSecurityAttribute we send on initial payload. Is there a way to extend this?

    1. How do we parse the token that we receive from Azure AD B2C using OWIN?

Any code sample would be handy.

TO make your question simpler.

The idea behind token is to parse the token and get 3 parts from the token

-Header : contain information about in which algorithm the token haven been encrypted

-Payload : information about the user

-Signature: it's the calculation of encryption of ( Header + Payload) using the Azure certificate or( your identity provider).

Next step the user sends request to your back-end along with JWT.

your back-end will parse the token and get certificate type then will preform HTTP request to your identity provider to get certificate

Next your back-end will construct the certificate option and try to do encryption for ( header + Payload) came from your token the output string must be exactly same Signature you received in the token from your front-end.

If every thing is okay now your back-end will start validating other attributes like Audience, Issuer if you configure your token to validate the Audience means your front-end required to provide token contain Audience(Application ID) exactly same your back-end as well as for issuer.

the question now how my back-end knows about certificate? Azure AD using OpenID connect, More information here since you configred you tenant in backend the auth packge will make a call to https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration to get the details about your identity provider and one important link as will is ("jwks_uri": "https://login.microsoftonline.com/common/discovery/keys") where the signature hosted. You can read and search more how to validate certificate JWT and check this https://codereview.stackexchange.com/questions/70005/authentication-with-jwt

moving to part 2 of validate more attributes. Since you are using OpenIdConnect, the package has class called OpenIdConnectEvents that you can trigger events and do what ever you want like this

.AddOpenIdConnect(o =>
    {
        //Additional config snipped
        o.Events = new OpenIdConnectEvents
        {
            OnTokenValidated = async ctx =>
            {
                //Get user's immutable object id from claims that came from Azure AD
                string oid = ctx.Principal.FindFirstValue("http://schemas.microsoft.com/identity/claims/objectidentifier");

                //Get EF context
                var db = ctx.HttpContext.RequestServices.GetRequiredService<AuthorizationDbContext>();

                //Check is user a super admin
                bool isSuperAdmin = await db.SuperAdmins.AnyAsync(a => a.ObjectId == oid);
                if (isSuperAdmin)
                {
                    //Add claim if they are
                    var claims = new List<Claim>
                    {
                        new Claim(ClaimTypes.Role, "superadmin")
                    };
                    var appIdentity = new ClaimsIdentity(claims);

                    ctx.Principal.AddIdentity(appIdentity);
                }
            }
        };
});

moving to part 3 parsing token in javascript

function parseJwt (token) {
            var base64Url = token.split('.')[1];
            var base64 = base64Url.replace(/-/g, '+').replace(/_/g, '/');
            return JSON.parse(window.atob(base64));
};

parsing token in C#

use this libary https://www.jsonwebtoken.io/

try {
  string jsonPayload = JWT.JsonWebToken.Decode(token, secretKey);
  Console.WriteLine(jsonPayload);
} catch (JWT.SignatureVerificationException) {
  Console.WriteLine("Invalid token!");
}

or manual

var jwtHandler = new JwtSecurityTokenHandler();
var jwtInput = txtJwtIn.Text;

//Check if readable token (string is in a JWT format)
var readableToken = jwtHandler.CanReadToken(jwtInput);

if(readableToken != true)
{
  txtJwtOut.Text = "The token doesn't seem to be in a proper JWT format.";
}
if(readableToken == true)
{
  var token = jwtHandler.ReadJwtToken(jwtInput);

  //Extract the headers of the JWT
  var headers = token.Header;
  var jwtHeader = "{";
  foreach(var h in headers)
  {
    jwtHeader += '"' + h.Key + "\":\"" + h.Value + "\",";
  }
  jwtHeader += "}";
  txtJwtOut.Text = "Header:\r\n" + JToken.Parse(jwtHeader).ToString(Formatting.Indented);

  //Extract the payload of the JWT
  var claims = token.Claims;
  var jwtPayload = "{";
  foreach(Claim c in claims)
  {
    jwtPayload += '"' + c.Type + "\":\"" + c.Value + "\",";
  }
  jwtPayload += "}";
  txtJwtOut.Text += "\r\nPayload:\r\n" + JToken.Parse(jwtPayload).ToString(Formatting.Indented);  
}

I hope that answer your qustions

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM