Can't setup OWIN + OneLogin + Bearer

2019-06-10 22:44发布

I'm trying to secure my WebAPI project with authorization tokens. I don't wanna use cookies, I want to only use Authorization header like this: Authorization: Bearer xxx_access_or_id_token_xxx. I'm using OneLogin OIDC as external provider. Here's my Startup.cs

using Microsoft.Owin;
using Microsoft.Owin.Security;
using Microsoft.Owin.Security.DataHandler.Encoder;
using Microsoft.Owin.Security.Jwt;
using Owin;
using System.Web.Http;   

public void Configuration(IAppBuilder app)
{
    var issuer = "https://openid-connect.onelogin.com/oidc/";
    var audience = ConfigurationManager.AppSettings["OneLoginClientId"];
    var secret = TextEncodings.Base64.Encode((TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["OneLoginClientSecret"])));

    app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions {
        AuthenticationMode = AuthenticationMode.Active,
        AllowedAudiences = new[] { audience },
        IssuerSecurityTokenProviders = new[] { new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret) }
    });

    HttpConfiguration httpConfig = new HttpConfiguration();
    WebApiConfig.Register(httpConfig);
    app.UseWebApi(httpConfig);
}

Also I have controllers:

public class ValueController: ApiController
{
    [HttpGet]
    [AllowAnonymous]
    public string NotSecure()
    {
        return "Not secure";
    }

    [HttpGet]
    [Authorize]
    public strnig Secure()
    {
        return "Secure";
    }
}

Ok, now let's have OneLogin part. After Authentication Flow, I got 5 fields: access_token, expires_in, id_token which is JWT, refresh_token and token_type.

Using jwt.io I can parse my id_token and I have something like this:

Header:

{
  "alg": "RS256",
  "typ": "JWT",
  "kid": "xxx"
}

Payload:

{
  "sub": "33827172",
  "email": "john.smith@company.com",
  "name": "John Smith",
  "iat": 1515083928,
  "exp": 1515091128,
  "aud": "onelogin_client_id",
  "iss": "https://openid-connect.onelogin.com/oidc"
}

I'm trying to send to my api request with Authorization token. I've tried to send both: access_token and id_token, but every time I call my secure actions, I've got 401.

How do i fix this?

Maybe here's something I missed?

Nuget:

Microsoft.Owin -v 3.1.0

Microsoft.Owin.* -v 3.1.0

System.IdentityModel.Tokens.Jwt -v 4.0.1

1条回答
爷的心禁止访问
2楼-- · 2019-06-10 23:35

Ok, so the problem was in Signature. Here's how I made it work:

public static class AuthConfig
{
    public static JwtBearerAuthenticationOptions GetOptions()
    {
        var keyResolver = new KeyResolver(System.Configuration.ConfigurationManager.AppSettings["OneLoginOpenIdConfigurationDomain"]); //https://<your_domain>.onelogin.com/oidc

        return new JwtBearerAuthenticationOptions
        {
            AuthenticationMode = AuthenticationMode.Active,
            TokenValidationParameters = new TokenValidationParameters
            {
                ValidIssuer = System.Configuration.ConfigurationManager.AppSettings["OneLoginJWTIssuer"], //https://openid-connect.onelogin.com/oidc
                ValidAudience = System.Configuration.ConfigurationManager.AppSettings["OneLoginClientId"],
                IssuerSigningKeyResolver = (token, securityToken, identifier, paramaters) => keyResolver.GetSigningKey(identifier)
            }
        };
    }
}

#region Helpers
public class KeyResolver
{
    private readonly OpenIdConnectConfiguration openIdConfig;
    private static readonly TaskFactory TaskFactory = new TaskFactory(CancellationToken.None, TaskCreationOptions.None, TaskContinuationOptions.None, TaskScheduler.Default);

    public KeyResolver(string domain)
    {
        var cm = new ConfigurationManager<OpenIdConnectConfiguration>($"{domain}/.well-known/openid-configuration");
        openIdConfig = TaskFactory.StartNew(async () => await cm.GetConfigurationAsync()).Unwrap().GetAwaiter().GetResult();
    }

    public SecurityKey GetSigningKey(SecurityKeyIdentifier identifier)
    {
        // Find the security token which matches the identifier
        var securityToken = openIdConfig.SigningTokens.FirstOrDefault(t =>
        {
            // Each identifier has multiple clauses. Try and match for each
            foreach (var securityKeyIdentifierClause in identifier)
                if (t.MatchesKeyIdentifierClause(securityKeyIdentifierClause))
                    return true;

            return false;
        });

        // Return the first key of the security token (if found)
        return securityToken?.SecurityKeys.FirstOrDefault();
    }

}
#endregion

And here's Startup.cs:

public class Startup
{
    public void Configuration(IAppBuilder app)
    {
        HttpConfiguration httpConfig = new HttpConfiguration();
        WebApiConfig.Register(httpConfig);

        app.UseJwtBearerAuthentication(AuthConfig.GetOptions());
        app.UseWebApi(httpConfig);
    }
}

Also I added this to WebApiConfig, but I'm not sure that really helps.

config.SuppressDefaultHostAuthentication();
config.Filters.Add(new HostAuthenticationFilter("Bearer"));

Nuget:

<package id="Microsoft.AspNet.Cors" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.AspNet.Identity.Core" version="2.2.1" targetFramework="net45" />
<package id="Microsoft.AspNet.Identity.EntityFramework" version="2.2.1" targetFramework="net45" />
<package id="Microsoft.AspNet.Identity.Owin" version="2.2.1" targetFramework="net45" />
<package id="Microsoft.AspNet.WebApi.Owin" version="5.2.3" targetFramework="net45" />
<package id="Microsoft.IdentityModel.Logging" version="1.1.5" targetFramework="net45" />
<package id="Microsoft.IdentityModel.Protocol.Extensions" version="1.0.4.403061554" targetFramework="net45" />
<package id="Microsoft.IdentityModel.Tokens" version="5.1.5" targetFramework="net45" />
<package id="Microsoft.Owin" version="3.1.0" targetFramework="net45" />
<package id="Microsoft.Owin.Host.SystemWeb" version="3.1.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security" version="3.1.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security.Cookies" version="3.1.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security.Jwt" version="3.1.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security.OAuth" version="3.1.0" targetFramework="net45" />
<package id="Microsoft.Owin.Security.OpenIdConnect" version="3.1.0" targetFramework="net45" />
<package id="Owin" version="1.0" targetFramework="net45" />
<package id="System.IdentityModel.Tokens.Jwt" version="4.0.4.403061554" targetFramework="net45" />

In my test System.IdentityModel.Tokens.Jwt with version bigger than 4.0.4 was not working, so leave it like this.

Also I'd like to thanks great manual from Auth0 and their Github.

查看更多
登录 后发表回答