在WebApi中验证Azure AD令牌

时间:2017-07-11 18:42:56

标签: azure-active-directory

我们正在构建一个客户将使用AAD令牌命中的webAPI服务。因此,此服务需要对令牌进行身份验证以确保其有效。 这是一个多租户场景。

我通过AAD文档并且几乎没有问题。如果有人能帮助回答,我们将不胜感激。

1)根据我的理解,该服务将从AAD下载公共签名令牌并缓存它们。这些用于验证令牌,这是正确的吗?

2)刷新缓存签名令牌的建议间隔是多少?另外,针对按需刷新案例AAD的建议是否在刷新期内轮换密钥?

3)我在https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-signing-key-rollover

找到的每个AAD示例代码
MetadataSerializer serializer = new MetadataSerializer()
                {
                    // Do not disable for production code
                    CertificateValidationMode = X509CertificateValidationMode.None
                };

启用证书验证不起作用,因为机器不信任AAD根证书,这里的建议是什么?我们是否需要在受信任的商店中手动安装证书?

4)在AAD示例代码@ https://docs.microsoft.com/en-us/azure/active-directory/develop/active-directory-signing-key-rollover中,它以同步方式下载这些签名令牌,是否有代码的异步版本?

1 个答案:

答案 0 :(得分:0)

AFAIK,该请求将获取Azure AD发布的令牌,该令牌由私钥签名。 Web API将自动使用Azure AD key endpoints中的公钥验证令牌(OWIN组件)。

要在Visual Studio 2013之后使用版本开发Web API,则无需手动验证令牌。以下是一段保护Web API的代码:

public void ConfigureAuth(IAppBuilder app)
{
    app.UseWindowsAzureActiveDirectoryBearerAuthentication(
        new WindowsAzureActiveDirectoryBearerAuthenticationOptions
        {
            Audience = ConfigurationManager.AppSettings["ida:Audience"],
            Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
            TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters() { ValidateIssuer = false }
        });
}

Here是保护多租户的Web API的完整代码示例的链接。

更新(支持不同控制器的密码和令牌认证)

public void ConfigureAuth(IAppBuilder app)
        {
            app.UseWindowsAzureActiveDirectoryBearerAuthentication(
                new WindowsAzureActiveDirectoryBearerAuthenticationOptions
                {
                    Audience = ConfigurationManager.AppSettings["ida:Audience"],
                    Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
                    TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters() { ValidateIssuer = false }
                });

            app.UsePasswordAuthentication();
        }

public class PasswordAuthorization : AuthorizeAttribute
{
    public override void OnAuthorization(HttpActionContext actionContext)
    {
        var principal = actionContext.RequestContext.Principal as ClaimsPrincipal;

        if (principal.FindFirst("authenticationType") == null)
        {
            actionContext.Response = new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.Unauthorized,
                Content = new StringContent("You are unauthorized to access this resource!")
            };

        }

    }
}

public class TokenAuthorization : AuthorizeAttribute
{

    public override void OnAuthorization(HttpActionContext actionContext)
    {
        var principal = actionContext.RequestContext.Principal as ClaimsPrincipal;

        if (principal.FindFirst("authenticationType") != null)
        {
            actionContext.Response = new HttpResponseMessage
            {
                StatusCode = HttpStatusCode.Unauthorized,
                Content = new StringContent("You are unauthorized to access this resource!")
            };

        }

    }
}


public class PasswordAuthenticationOptions : AuthenticationOptions
{
    public PasswordAuthenticationOptions() : base("password")
    { }
}

public class PasswordAuthenticationHandler : AuthenticationHandler<PasswordAuthenticationOptions>
{
    protected override async Task<AuthenticationTicket> AuthenticateCoreAsync()
    {
        bool authorized = await Task<bool>.Run(() => IsAuthorised(Request.Headers));
        if (authorized)
        {
            AuthenticationProperties authProperties = new AuthenticationProperties();
            authProperties.IssuedUtc = DateTime.UtcNow;
            authProperties.ExpiresUtc = DateTime.UtcNow.AddDays(1);
            authProperties.AllowRefresh = true;
            authProperties.IsPersistent = true;
            IList<Claim> claimCollection = new List<Claim>
            {
                new Claim(ClaimTypes.Name, "Andras")
                , new Claim(ClaimTypes.Country, "Sweden")
                , new Claim(ClaimTypes.Gender, "M")
                , new Claim(ClaimTypes.Surname, "Nemes")
                , new Claim(ClaimTypes.Email, "hello@me.com")
                , new Claim(ClaimTypes.Role, "IT")
                , new Claim("authenticationType", "password")
            };
            ClaimsIdentity claimsIdentity = new ClaimsIdentity(claimCollection, "Custom");
            AuthenticationTicket ticket = new AuthenticationTicket(claimsIdentity, authProperties);
            return ticket;
        }

        return null;
    }

    private bool IsAuthorised(IHeaderDictionary requestHeaders)
    {
        string[] username,password;
        bool usernamePresent = requestHeaders.TryGetValue("username", out username);
        bool passwordPresent = requestHeaders.TryGetValue("password", out password);

        if (usernamePresent& passwordPresent)
        {
            if("user1".Equals(username[0])&&"pass".Equals(password[0]))
                return true;
        }

        return false;
    }
}


public class PasswordAuthMiddleware : AuthenticationMiddleware<PasswordAuthenticationOptions>
{
    public PasswordAuthMiddleware(OwinMiddleware nextMiddleware, PasswordAuthenticationOptions authOptions)
        : base(nextMiddleware, authOptions)
    { }

    protected override AuthenticationHandler<PasswordAuthenticationOptions> CreateHandler()
    {
        return new PasswordAuthenticationHandler();
    }
}

public static class PassworAuthenticationExtension
{
    public static void UsePasswordAuthentication(this IAppBuilder appBuilder)
    {
        appBuilder.Use<PasswordAuthMiddleware>(new PasswordAuthenticationOptions());
    }
}

使用密码进行身份验证的API控制器:

[PasswordAuthorization]
public class Values2Controller : ApiController
{
    // GET: api/Values2
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    } 
}

使用AAD进行身份验证的API控制器:

[TokenAuthorization]
public class ValuesController : ApiController
{ 
    // GET api/values
    public IEnumerable<string> Get()
    {
        return new string[] { "value1", "value2" };
    }
}

更新(限制租户)

app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
    Audience = ConfigurationManager.AppSettings["ida:Audience"],
    Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
    TokenValidationParameters = new System.IdentityModel.Tokens.TokenValidationParameters() {
        ValidateIssuer = true,
        ValidIssuers =new string[] { "https://sts.windows.net/{tenantId}/" } 
    }
});