32

I have successfully added OAuth to my WebAPI 2 project using OWIN. I receive tokens and can use them in the HTTP Header to access resources.

Now I want to use those tokens also on other channels for authentication that are not the standard HTTP requests that the OWIN template is made for. For example, I am using WebSockets where the client has to send the OAuth Bearer Token to authenticate.

On the server side, I receive the token through the WebSocket. But how can I now put this token into the OWIN pipeline to extract the IPrincipal and ClientIdentifier from it? In the WebApi 2 template, all this is abstracted for me, so there is nothing I have to do to make it work.

So, basically, I have the token as a string and want to use OWIN to access the user information encoded in that token.

Thank you in advance for the help.

4 Answers 4

31

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"]; ... } 
Sign up to request clarification or add additional context in comments.

1 Comment

No idea how you figured out this. I can`t find any documentation about it, but thanks a lot!
10

By default, OWIN use ASP.NET machine key data protection to protect the OAuth access token when hosted on IIS. You can use MachineKey class in System.Web.dll to unprotect the tokens.

public class MachineKeyProtector : IDataProtector { private readonly string[] _purpose = { typeof(OAuthAuthorizationServerMiddleware).Namespace, "Access_Token", "v1" }; public byte[] Protect(byte[] userData) { throw new NotImplementedException(); } public byte[] Unprotect(byte[] protectedData) { return System.Web.Security.MachineKey.Unprotect(protectedData, _purpose); } } 

Then, construct a TicketDataFormat to get the AuthenticationTicket object where you can get the ClaimsIdentity and AuthenticationProperties.

var access_token="your token here"; var secureDataFormat = new TicketDataFormat(new MachineKeyProtector()); AuthenticationTicket ticket = secureDataFormat.Unprotect(access_token); 

To unprotect other OAuth tokens, you just need to change the _purpose content. For detailed information, see OAuthAuthorizationServerMiddleware class here: http://katanaproject.codeplex.com/SourceControl/latest#src/Microsoft.Owin.Security.OAuth/OAuthAuthorizationServerMiddleware.cs

if (Options.AuthorizationCodeFormat == null) { IDataProtector dataProtecter = app.CreateDataProtector( typeof(OAuthAuthorizationServerMiddleware).FullName, "Authentication_Code", "v1"); Options.AuthorizationCodeFormat = new TicketDataFormat(dataProtecter); } if (Options.AccessTokenFormat == null) { IDataProtector dataProtecter = app.CreateDataProtector( typeof(OAuthAuthorizationServerMiddleware).Namespace, "Access_Token", "v1"); Options.AccessTokenFormat = new TicketDataFormat(dataProtecter); } if (Options.RefreshTokenFormat == null) { IDataProtector dataProtecter = app.CreateDataProtector( typeof(OAuthAuthorizationServerMiddleware).Namespace, "Refresh_Token", "v1"); Options.RefreshTokenFormat = new TicketDataFormat(dataProtecter); } 

4 Comments

so this means you would have to use the same protector on both the webapi and the other app (say some stand-alone signalR console app)?
And what about self-hoseted owin app where we don't have access to System.Web?
@MahmoudMoravej Then you have to use the DPAPI DataProtector instead of the MachineKeyDataProtector.
Yes, exactly like what I explained in the following answer
1

in addition to johnny-qian answer, using this method is better to create DataProtector. johnny-qian answer, depends on IIS and fails on self-hosted scenarios.

using Microsoft.Owin.Security.DataProtection; var dataProtector = app.CreateDataProtector(new string[] { typeof(OAuthAuthorizationServerMiddleware).Namespace, "Access_Token", "v1" }); 

Comments

0

What is your token like, is it an encrypt string or a formatted string, what is it format?

I my code:

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

The c.Ticket is always null.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.