如何验证asp.net MVC OWIN从azure AD B2C接收到的id_token

时间:2019-04-06 08:18:25

标签: c# azure owin

我们有一个安全要求,我们必须验证从Azure AD B2C收到的ID令牌。我们需要至少对它们进行验证

customSecurityLevel 受众不早于“到期时间”,发行人,现时

在查看asp.net MVC OWIN中间件时,我注意到OpenIdConnectAuthenicationOptions提供了以下这些功能:

preprocess_input

作为OWIN的新手,我试图在下面进行了解:

  1. OWIN中间件是否神奇地验证了我们从Azure AD B2C接收到的令牌,还是需要手动执行验证: https://azure.microsoft.com/en-us/resources/samples/active-directory-dotnet-webapi-manual-jwt-validation/

  2. 应该在什么时间进行令牌验证,即AuthorizationCodeReceived事件或在Azure AD B2C重定向URL上配置的重定向控制器/操作(页面)上?

  3. 我们需要验证TokenValidationParameters支持的更多属性,例如我们在初始有效载荷上发送的customSecurityAttribute。有办法扩展吗?

    1. 我们如何使用OWIN解析从Azure AD B2C收到的令牌?

任何代码示例都将很方便。

1 个答案:

答案 0 :(得分:1)

使您的问题更简单。

令牌背后的想法是解析令牌并从令牌中获取3部分

-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).

下一步,用户将请求与JWT一起发送到您的后端。

您的后端将解析令牌并获取证书类型,然后向身份提供者执行HTTP请求以获取证书

接下来,您的后端将构造证书选项,并尝试对来自令牌的(标头+有效负载)进行加密,输出字符串必须与您从前端在令牌中收到的签名完全相同。

如果一切正常 现在您的后端将开始验证其他属性,例如Audience,Issuer 如果您配置令牌以验证受众群体,则意味着提供令牌所包含的受众群体(应用程序ID)所需的前端与后端以及发行方完全相同。

现在的问题是我的后端如何了解证书? 使用OpenID connect,More information here的Azure AD 由于您在后端配置了承租人,因此身份验证包将致电https://login.microsoftonline.com/{tenant}/.well-known/openid-configuration以获取有关身份提供者的详细信息 一个重要的链接将是("jwks_uri": "https://login.microsoftonline.com/common/discovery/keys")托管签名的位置。 您可以阅读和搜索更多如何验证证书JWT并进行检查 https://codereview.stackexchange.com/questions/70005/authentication-with-jwt

移至第2部分,验证更多属性。 由于您使用的是OpenIdConnect,因此该软件包具有名为OpenIdConnectEvents的类,您可以触发事件并执行所需的操作

.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);
                }
            }
        };
});

移至第3部分 在javascript中解析令牌

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

使用C#解析令牌

使用此库https://www.jsonwebtoken.io/

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

或手册

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);  
}

我希望能回答您的疑问