Get Iprincipal from Oauth Bearer Token in Owin

Get IPrincipal from OAuth Bearer Token in OWIN

I found a part of the solution in this blog post: http://leastprivilege.com/2013/10/31/retrieving-bearer-tokens-from-alternative-locations-in-katanaowin/

So I created my own Provider as follows:

public class QueryStringOAuthBearerProvider : OAuthBearerAuthenticationProvider
{
public override Task RequestToken(OAuthRequestTokenContext context)
{
var value = context.Request.Query.Get("access_token");

if (!string.IsNullOrEmpty(value))
{
context.Token = value;
}

return Task.FromResult<object>(null);
}
}

Then I needed to add it to my App in Startup.Auth.cs like this:

OAuthBearerOptions = new OAuthBearerAuthenticationOptions()
{
Provider = new QueryStringOAuthBearerProvider(),
AccessTokenProvider = new AuthenticationTokenProvider()
{
OnCreate = create,
OnReceive = receive
},
};

app.UseOAuthBearerAuthentication(OAuthBearerOptions);

With a custom AuthenticationTokenProvider, I can retrieve all other values from the token early in the pipeline:

public static Action<AuthenticationTokenCreateContext> create = new Action<AuthenticationTokenCreateContext>(c =>
{
c.SetToken(c.SerializeTicket());
});

public static Action<AuthenticationTokenReceiveContext> receive = new Action<AuthenticationTokenReceiveContext>(c =>
{
c.DeserializeTicket(c.Token);
c.OwinContext.Environment["Properties"] = c.Ticket.Properties;
});

And now, for example in my WebSocket Hander, I can retrieve ClientId and others like this:

IOwinContext owinContext = context.GetOwinContext();
if (owinContext.Environment.ContainsKey("Properties"))
{
AuthenticationProperties properties = owinContext.Environment["Properties"] as AuthenticationProperties;
string clientId = properties.Dictionary["clientId"];
...
}

C#/OWIN/ASP.NET: can I *manually* generate and get a valid bearer token string in my API code?

I did figure out the code to make this work. One could argue I'm "not using OAuth right", but strictly, this code WILL accomplish what I want - to generate a token in code at any arbitrary point and get the string.

First, as I said, I have to provide access to the OAuthBearerAuthenticationOptions class instance. When the OAuth server initializes I'm guessing it populates this class with all of the various objects used for tokens. The key is that we do have access to Protect and Unprotect which can both encode and decode bearer tokens directly.

This code will generate a token assuming that oabao is the OAuthBearerAuthenticationOptions class that has been passed to the OAuthAuthorizationServer instance:

Microsoft.Owin.Security.AuthenticationTicket at = new Microsoft.Owin.Security.AuthenticationTicket(new ClaimsIdentity("Bearer", "http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name", "http://schemas.microsoft.com/ws/2008/06/identity/claims/role"),
new Microsoft.Owin.Security.AuthenticationProperties
{
AllowRefresh = true,
IsPersistent = true,
IssuedUtc = DateTime.UtcNow,
ExpiresUtc = DateTime.UtcNow.AddDays(1) // whenever you want your new token's expiration to happen
});

// add any claims you want here like this:
at.Identity.AddClaim(new Claim("userRole", role));
// and so on

string token = oabao.AccessTokenFormat.Protect(at);

// You now have the token string in the token variable.

consuming oAuth bearer issued by OWIN from asp.net core

You could set the OAuthValidation AccessTokenFormat to use a MachineKey DataProtectionProvider and DataProtector which will protect and unprotect your bearer tokens. You will need to implement the MachineKey DataProtector. This guy already did it https://github.com/daixinkai/AspNetCore.Owin/blob/master/src/DataProtection/src/AspNetCore.DataProtection.MachineKey/MachineKeyDataProtectionProvider.cs.

public void ConfigureServices(IServiceCollection services){
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
ConfigureAuth(services);

string machineKey = @"<?xml version=""1.0"" encoding=""utf-8"" ?>
<machineKey decryption=""Auto"" decryptionKey =""DEC_KEY"" validation=""HMACSHA256"" validationKey=""VAL_KEY"" />";
var machineKeyConfig = new XmlMachineKeyConfig(machineKey);
MachineKeyDataProtectionOptions machinekeyOptions = new MachineKeyDataProtectionOptions();
machinekeyOptions.MachineKey = new MachineKey(machineKeyConfig);
MachineKeyDataProtectionProvider machineKeyDataProtectionProvider = new MachineKeyDataProtectionProvider(machinekeyOptions);
MachineKeyDataProtector machineKeyDataProtector = new MachineKeyDataProtector(machinekeyOptions.MachineKey);

//purposes from owin middleware
IDataProtector dataProtector =
machineKeyDataProtector.CreateProtector("Microsoft.Owin.Security.OAuth",
"Access_Token", "v1");

services.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddOAuthValidation(option=> {
option.AccessTokenFormat = new OwinTicketDataFormat(new OwinTicketSerializer(), dataProtector); })

It's important to keep the same DataProtector "purposes" Owin uses in the OAuthAuthorizationServerMiddleware so the data is encrypted/decrypted correctly. Those are "Microsoft.Owin.Security.OAuth", "Access_Token" and "v1". (see https://stackoverflow.com/a/29454816/2734166).

And finally you will have to migrate the Owin TicketSerializer (and maybe the TicketFormat too) since the one in NetCore is slightly different. You can grab it from here:

https://github.com/aspnet/AspNetKatana/blob/e2b18ec84ceab7ffa29d80d89429c9988ab40144/src/Microsoft.Owin.Security/DataHandler/Serializer/TicketSerializer.cs

I got this working recently. Basically authenticating to a .NET 4.5 Owin API and running a resource API in NET Core using the same token. I'll try to share the code in github as soon as I clean it up.

As far as I know it's not recommended to keep the old machine key data protector, but to migrate to the new ones from NET Core. Sometimes this is not possible. In my case I have too many APIs already in production, so I'm trying some new NET Core APIs to work with the legacy ones.

Validate our “Owin OAuth2 Bearer Token” from other Application

Just sharing some info on how I solved this..

Just create a new endpoint in your Web.Api or you can even filter your context.Request.Path for your "/tokencheck/" path.

then you can finally:

context.DeserializeTicket(context.Token);
context.OwinContext.Environment["Properties"] = context.Ticket.Properties;

This question will help you develop a working solution:

Get IPrincipal from OAuth Bearer Token in OWIN

Is it possible to renew the access token in OWIN

I don't believe this is possible. An Oauth token can't be modified once it is created as modifying would result in the signature becoming invalid (which is a good thing).

The consuming application would be required to renew the token (re-authenticating, use a refresh token etc) which would be possible to achieve if the consuming application and API are under your control, otherwise this may not be a practical limitation to impose on your API consumers.

An alternative is to avoid storing the changeable claims in the access token, but rather populate the ClaimsIdentity object (in the IPrincipal) that gets hydrated from the access token with additional claims when a request is received. This would allow you to update the values within the API, but also not require your consumers to obtain a new access token every time a claim needs to change. The downside is you need to load the claims on every request. You also need to have a value in the access token that is guaranteed to identify the user that can not ever change.

You could do this using an OwinMiddleware class:

public override async Task Invoke(IOwinContext context)
{
ClaimsIdentity identity = context.Authentication.User?.Identity as ClaimsIdentity;
identity.AddClaims(new Claim("Name", "Value));
}

How to share owin token from .Net Framework with .Net Core 6

As far as I know, asp.net core also support OWIN authentication, you could use the oauth's settings inside the asp.net core application. For example, both the Asp.net and asp.net core support the JWT authentication, if the token's issuer and secect or other settings is the same, we could use access token.



Related Topics



Leave a reply



Submit