Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

ASP.NET Identity 2.0 Authenticate against our own bearer server

All,

I've got a security server which sole purpose is to provide bearer tokens from a single endpoint: http://example.com/token

Example request:

POST http://example.com/token HTTP/1.1
User-Agent: Fiddler
Content-Type: x-www-form-urlencoded
Host: example.com
Content-Length: 73

grant_type=password&[email protected]&password=examplePassword

Example response:

HTTP/1.1 200 OK
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json;charset=UTF-8
Expires: -1
Server: Microsoft-IIS/10.0
X-Powered-By: ASP.NET
Date: Tue, 16 Aug 2016 12:04:39 GMT
{
  "access_token": "xxxx",
  "token_type": "bearer",
  "expires_in": 17999,
  "refresh_token": "xxxx",
  ".issued": "Tue, 16 Aug 2016 12:04:38 GMT",
  ".expires": "Tue, 16 Aug 2016 17:04:38 GMT"
}

We have an angular application which uses this endpoint to authenticate, and does so just fine.

What we are trying to achieve without much success is to create an MVC application which uses the same server to authenticate, we'd like the code to sit on top of Identity 2.0 if possible.

In our AccountController (example project) we have our Login(LoginModel model) method which handles login and looks like this (same as example project template):

var result = await _signInManager.PasswordSignInAsync(model.UserName, model.Password, model.RememberMe, shouldLockout: false);

We have our own implemention of IUserStore, UserManager, SignInManager.

I've considered overriding

public Task<SignInStatus> PasswordSignInAsync(string userName, string password, bool isPersistent, bool shouldLockout) on `SignInManager<,>` and make a web call across to the security server.

The default implementation of PasswordSignInAsync calls UserManager.FindByNameAsync however this would mean I'd have to expose a lookup method on my security server to confirm a username exists which really doesn't sound good.

I must be missing something and I know it wouldn't be this complicated, our MVC app needs to use cookie authentication but also maintain the bear token for subsequent calls to our other resource server.

(I appreciate I might be mixing up technologies here, hence the question).

This is also running on OWIN.

like image 870
Steven Yates Avatar asked Aug 16 '16 12:08

Steven Yates


1 Answers

In this case, I don't think you should use Identity 2.0 in your MVC application. You should create an AuthenticationClient to call your authentication server, you could also use this library to build such a client https://www.nuget.org/packages/Thinktecture.IdentityModel.Client/

public class AuthenticationClient {
    public ClaimsIdentity GetClaimsIdentity(string username, string password) {
         //Call your authentication server to get your token and also claims associated with your identity.
         //You can look at an example code how to do it: https://github.com/IdentityServer/IdentityServer3.Samples/blob/master/source/Clients/ConsoleResourceOwnerClient/Program.cs
         //and create a ClaimsIdentity object out of those information

         var identity = GetIdentity();

         //The key point here is to add AccessToken and RefreshToken as custom claims to your identity so you retrieve these tokens back on subsequent requests.
        identity.AddClaim(new Claim("access_token", accessToken));
        identity.AddClaim(new Claim("refresh_token", refreshToken));
    }
}

This will fulfill your requirements:

Use cookie authentication but also maintain the bear token for subsequent calls to our other resource server.

Your Login method on AccountController should be like this:

public ActionResult Login(LoginModel model)
{
     var identity = authenticationClient.GetClaimsIdentity(model.UserName, model.Password);

     if (identity == null) {
         return new HttpUnauthorizedResult();
     }
     //Sign in the user
     var ctx = Request.GetOwinContext();
     var authenticationManager = ctx.Authentication;
     authenticationManager.SignIn(identity);

     return new HttpStatusCodeResult(HttpStatusCode.OK);
}

I assume that you already register this middleware to use Cookie authentication:

public void ConfigureAuth(IAppBuilder app)
{
   app.UseCookieAuthentication(new CookieAuthenticationOptions
   {
      AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
      LoginPath = new PathString("/Account/Login")
   });
}

Now for all subsequent requests, you could retrieve back the access token to call your resource server from your ClaimsIdentity:

User.Identity.Claims.FirstOrDefault(x => x.Type == "access_token");
like image 100
Khanh TO Avatar answered Sep 22 '22 14:09

Khanh TO