Register front-end Web API 2 from multiple API clients with OWIN authentication

I need the following architecture (I made up the product name for this example):

Web API 2 application running on the same server http://api.prettypictures.com

The MVC 5 client application runs on another server http://www.webpics.com

I would like the www.webpics.com client application to use the Pretty Pictures API for:

  • Register new accounts with username and password.
  • Register new accounts on Facebook / Google / Twitter / Microsoft.
  • To come in
  • Image Retrieval

All of the above works with the exception of registering external accounts in Facebook, Google, etc.

I cannot process the correct thread to create an external account from a separate API client.

I have studied most of the documents available in the authentication flow, for example: enter image description here

I read almost everything I can about the new Identity model in OWIN.

I reviewed the SPA template in Visual Studio 2013. It demonstrates how to do most of what I need, but only when the client and the API are on the same host; if I want several clients accessing my API and can allow users to register through Google, etc., this does not work, and as far as I can tell, the OWIN authentication flow is breaking.

Here is the thread so far:

  • User browses www.webpics.com/Login
  • www.webpics.com calls api.prettypictures.com/Account/ExternalLogins (with returnUrl to return to the callback at www.webpics.com ) and displays the resulting user links.
  • User clicks the google button
  • The browser redirects the name of the provider to api.prettypictures.com/Account/ExternalLogin , etc.
  • ExternalLogin API action calls google.com
  • The browser redirects to google.com
  • The user enters their username and password (if they are not already logged in to google.com )
  • google.com now represents a security permission: "api.prettypictures.com" would like to access your email address, name, wife, children, etc. This is normal?
  • The user clicks “Yes” and returns to api.prettypictures.com/Account/ExternalLogin with the cookie set by Google.

This is where I am stuck. What needs to happen next is somehow the client application must be notified that the user has successfully authenticated with google.com and has been given an access code for one access in order to exchange the access token later. The client application should be able, if necessary, to invite the user to the username in order to associate it with the name google.com .

I do not know how to do that.

In fact, at that moment, the browser ends at api.prettypictures.com/Account/ExternalLogin endpoint after a callback from Google. The API is signed for Google, but the client does not know how to deal with it. Should I transfer this cookie to www.webpics.com ?

In the SPA application, this is done through AJAX, and google.com returns the token as a fragment of the URL, and everything works well, because all this is on the same domain. But this challenges most of the fact that the API can use multiple clients.

Help!

+67
asp.net-mvc asp.net-identity owin asp.net-web-api2
Jan 16 '14 at 16:41
source share
1 answer

Update: everything has changed since I wrote this post in January : MSFT released its official OpenID middleware to connect the client, and I worked a lot with @manfredsteyer to adapt the OAuth2 authorization server built into Katana into OpenID connect. This combination provides a much easier and much more powerful solution that does not require any custom client code and is 100% compatible with standard OAuth2 / OpenID clients. The various steps that I mentioned in January can now be replaced with a few lines:

Server:

app.UseOpenIdConnectServer(options => { options.TokenEndpointPath = new PathString("/connect/token"); options.SigningCredentials.AddCertificate(certificate); options.Provider = new CustomOpenIdConnectServerProvider(); }); 

Client:

 app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions { Authority = "http://localhost:55985/", ClientId = "myClient", ClientSecret = "secret_secret_secret", RedirectUri = "http://localhost:56854/oidc" }); 

You can find all the details (and different samples) in the GitHub repository:

https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server

https://github.com/aspnet-contrib/AspNet.Security.OpenIdConnect.Server/tree/dev/samples/Nancy




Josh, you are definitely on the right track, and your implementation of delegated / federated authentication seems pretty good (I assume you used the predefined OWIN middleware from Microsoft.Owin.Security.Facebook/Google/Twitter ).

What you need to do is create your own OAuth2 authorization server . You have many options to achieve this, but the simplest one is probably to connect OAuthAuthorizationServerMiddleware to your OWIN startup class. You will find it in the Microsoft.Owin.Security.OAuth Nuget package.

Although it would be best practice to create a separate project (often called an “AuthorizationServer”), I personally prefer to add it to my “API project” when it is not intended for use in multiple APIs (here you would insert it into the hosting project “api.prettypictures .com ").

In the Katana repository you will find a great sample:

https://katanaproject.codeplex.com/SourceControl/latest#tests/Katana.Sandbox.WebServer/Startup.cs

 app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions { AuthorizeEndpointPath = new PathString("/oauth2/authorize"), TokenEndpointPath = new PathString("/oauth2/token"), ApplicationCanDisplayErrors = true, AllowInsecureHttp = true, Provider = new OAuthAuthorizationServerProvider { OnValidateClientRedirectUri = ValidateClientRedirectUri, OnValidateClientAuthentication = ValidateClientAuthentication, OnGrantResourceOwnerCredentials = GrantResourceOwnerCredentials, }, AuthorizationCodeProvider = new AuthenticationTokenProvider { OnCreate = CreateAuthenticationCode, OnReceive = ReceiveAuthenticationCode, }, RefreshTokenProvider = new AuthenticationTokenProvider { OnCreate = CreateRefreshToken, OnReceive = ReceiveRefreshToken, } }); 

Feel free to browse the entire project to see how the authorization consent form was implemented using simple Razor files. If you prefer a higher-level structure such as ASP.NET MVC or NancyFX, create your own AuthorizationController and Authorize controller (make sure you accept GET and POST) and use Attribute Routing according to the AuthorizeEndpointPath parameter defined in your OAuth2 (ie [Route("oauth2/authorize")] in my example, where I changed AuthorizeEndpointPath to use oauth2/ as the base of the path).

Another thing you need to do is add the OAuth2 authorization client to your web application. Unfortunately, there is no general OAuth2 customer support in Katana, and you will have to create your own. I personally submitted a proposal to the Katan team, but was refused. But don't panic, it's pretty easy to do:

Copy the appropriate files from the Microsoft.Owin.Security.Google repository located there: https://katanaproject.codeplex.com/SourceControl/latest#src/Microsoft.Owin.Security.Google/GoogleOAuth2AuthenticationHandler.cs

You will need GoogleOAuth2AuthenticationHandler , GoogleOAuth2AuthenticationMiddleware , GoogleOAuth2AuthenticationOptions , GoogleAuthenticationExtensions (you will need to remove the first 2 methods that correspond to the Google OpenID implementation), IGoogleOAuth2AuthenticationProvider , GoogleOAuth2ReturnEndpointContext , GoogleOAuth2AuthenticationProvider , GoogleOAuth2AuthenticatedContext , IGoogleOAuth2AuthenticationProvider , GoogleOAuth2ReturnEndpointContext , GoogleOAuth2AuthenticationProvider , GoogleOAuth2AuthenticatedContext Once you have inserted these files into your project by placing "webpics.com", rename them accordingly and change the URLs of the authorization and access tokens to GoogleOAuth2AuthenticationHandler to match the ones you defined on the OAuth2 authorization server .

Then add the Use method from your renamed / custom GoogleAuthenticationExtensions to your OWIN launcher class. I suggest using AuthenticationMode.Active so that your users are directly redirected to the OAuth2 API authorization endpoint. Therefore, you must suppress the "api.prettypictures.com/Account/ExternalLogins" roundtrip and allow the OAuth2 middleware to modify the 401 response to redirect clients to your API.

Good luck. And do not be shy if you need more information;)

+42
Jan 22 '14 at 20:01
source share



All Articles