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