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:
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/
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?
We need to validate more attributes that TokenValidationParameters supports eg customSecurityAttribute we send on initial payload. Is there a way to extend this?
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.