6

I've two Web APIs:

  • Identity API. It implements authentication, user registration and OAuth client registration and authentication.
  • Product API. A set of RESTful APIs to work with my business.

I use the Identity API to authenticate resource owners (i.e. users with user+password credentials) to get a bearer token using OAuth2 OWIN middleware.

My issue is once I get an access token from the Identity API, I want to use it to also authorize requests to the Product API.

Both APIs are accessing the same database and both are for now in the same machine.

When I try to perform a request against the Product API I get as response message "Authorization has been denied for this request", while performing requests to Identity API works flawlessly.

The point is I've derived OAuthAuthorizationServerProvider to meet some custom authentication/authorization requirements, but it never arrives to this point (ValidateClientAuthentication and GrantResourceOwnerCredentials methods are never called) when issuing requests to the Product API.

I've already discarded that the order of OWIN middleware may be affecting the authentication flow: both APIs are configured in the same exact way.

Some days ago...

... before trying to work this way, I was thinking about creating both a custom OAuthAuthorizationServerProvider and ASP.NET Identity user store to actually query the Identity API internally, thus, both authentication and authorization would be verified in the OWIN app which issued the access token.

I've already implemented a custom user store to ASP.NET Identity (GitHub repository), and I've not already implemented a OAuthAuthorizationServerProvider to issue HTTP requests instead of using the underlying database directly too.

Anwyay, I would like to know if I can avoid going this way for a while, and if I can issue access tokens from an OWIN app and consume access tokens from a different OWIN app with also a different host and port.

Update: Debugging System.Web.Http

I've download System.Web.Http source code from ASP.NET Web Stack GitHub repository and I've also compiled it, and I've linked the compiled assembly to my Product API WebAPI project to debug AuthorizeAttribute.

The whole bearer token is received but actionContext.ControllerContext.RequestContext.Principal is null. I suspect that some OAuth-related middleware isn't decrypting and assigning the user to the whole property.

The point is the same token will work on the Identity API.

Check the following screenshot where I can demonstrate the bearer token is being received:

enter image description here

Update 2: Identity API can authorize requests using the emitted access token...

I can confirm that the access token works to authorize requests to Identity API's resources (for example, I've implemented an UserController to let the Identity API register and manage users, and most controller actions are marked with [Authorize] attribute...).

Matías Fidemraizer
  • 63,804
  • 18
  • 124
  • 206

1 Answers1

13

Yes, you can decouple the authorization server and the resource server. They could live in different owin apps and not only that, they can even live on different servers.

The authorization server would be responsible to issue access tokens to your client application and, eventually, manage users and roles.

The resource server would host the protected resource, accepting access tokens released by the authorization server.

Looking at your infrastructure your Identity API would be the authorization server and Product API would be your resource server.

The authorization server needs to configure and use an implementation of OAuthAuthorizationServerProvider.
In your startup you would do something like this:

app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);

var OAuthOptions = new OAuthAuthorizationServerOptions
{
    AllowInsecureHttp = true,
    TokenEndpointPath = new PathString("/oauth/Token"),
    AccessTokenExpireTimeSpan = TimeSpan.FromHours(8),
    Provider = new MyAuthorizationServerProvider(),
    // RefreshTokenProvider = new MyRefreshTokenProvider(DateTime.UtcNow.AddHours(8))
};

app.UseOAuthAuthorizationServer(OAuthOptions);
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());

As you can see I've used MyAuthorizationServerProvider as custom provider for the authentication.

It is responsible to validate the client (ValidateClientAuthentication) and grant the authorization to access the resource (GrantResourceOwnerCredentials).

GrantResourceOwnerCredentials will check if the credentials of the client (username & password) are valid and release a valid token:

var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);

The AuthenticationTicket receives a ClaimsIdentity object and a collection of AuthenticationProperties.

You would normally add the username claim and the roles here:

ClaimsIdentity identity = new ClaimsIdentity(context.Options.AuthenticationType);
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim(ClaimTypes.Role, "Users"));

and, eventually, other types of claims you might want to use.
The AuthenticationProperties collection would define your extended info you want to pass to the client:

var props = new AuthenticationProperties(new Dictionary<string, string>
{
    { 
    "name", "John"
    },
    { 
    "surname", "Smith"
    },
    { 
    "age", "40"
    },
    { 
    "gender", "Male"
    }
});

You can check this github repo to have a look at the implementation.

Your resource server, the RESTful APIs responsible to manage your business info, won't have to re-implement the whole authorization/authentication layer.

In your startup (Product API) you would simply instruct the api to use and consume bearer tokens:

public void Configuration(IAppBuilder app)
{
    HttpConfiguration config = new HttpConfiguration();

    OAuthBearerAuthenticationOptions OAuthBearerOptions = new OAuthBearerAuthenticationOptions();
    app.UseOAuthBearerAuthentication(OAuthBearerOptions);

    WebApiConfig.Register(config);
    app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
    app.UseWebApi(config);
}

And your protected resource would be something like this:

[Authorize(Roles = "Users")]
public class ProductsController : ApiController
{
    public Models.Product GetProduct(string id)
    {
        var identity = User.Identity as ClaimsIdentity;

        return new Models.Product();
    }
}

As you can see I've used the Authorize attribute cause I only want to authorize a certain type of user.

NOTE:

If you want to host the authorization server and the resource server on to different machines, you have to make sure you're sharing the same machineKey on both server or the resource server won't be able to decrypt the bearer token released by authorization server:

<system.web>
    <compilation debug="true" targetFramework="4.5" />
    <httpRuntime targetFramework="4.5" />
    <machineKey validationKey="VALUE GOES HERE" 
                decryptionKey="VALUE GOES HERE" 
                validation="SHA1" 
                decryption="AES"/>
</system.web>

I would suggest you to read this article to find a really good explanation about all the pieces involved.

Taisser also as another article where he goes through the same process using Json Web Tokens to achieve the same result.

LeftyX
  • 35,328
  • 21
  • 132
  • 193
  • Your effort is very appreciated, BTW I feel like you've answer to a question like "how to work with ASP.NET Identity and OAuth" rather than my actual issue, excepting the part of "yes, you can" (I already knew the part of machine key). The issue is I'm receiving the whole `"Authorization has been denied for this request"` error message. Anyway, your answer should be very interesting for future readers – Matías Fidemraizer Dec 03 '15 at 08:30
  • I'm going to try to fix the issue again, maybe I'll compile System.Web.Http myself to be able to debug the authorization filter, so I'll be able to understand what's not going in the right way during authorization pipeline.. – Matías Fidemraizer Dec 03 '15 at 08:31
  • About the CORS part, since we're talking about API-to-API authorization, I doubt that it would affect my current scenario. This would be useful to authenticate/authorize from Web browsers, am I mistaken? – Matías Fidemraizer Dec 03 '15 at 08:32
  • I think I fail to really understand your problem. I guess you have a client which requests a token from the authentication server and wants to access some resources available in another layer (api). Am I right? – LeftyX Dec 03 '15 at 12:53
  • Exactly, this is the actual scenario – Matías Fidemraizer Dec 03 '15 at 12:55
  • And it should work the way I have described it. – LeftyX Dec 03 '15 at 12:59
  • I know it. This is what I expect too, I had the same scenario like you described in your answer before asking the question. The issue is I don't know why it's not working – Matías Fidemraizer Dec 03 '15 at 13:01
  • I cannot show you my code cause it's not an open source project but you can download this [repo](https://github.com/tjoudeh/AngularJSAuthentication) and play with it. The resource server has a protected [controller](https://github.com/tjoudeh/AngularJSAuthentication/tree/master/AngularJSAuthentication.ResourceServer/Controllers) which is never called but you can change the angular code to make it work. – LeftyX Dec 03 '15 at 13:11
  • Alternatively if you provide some code we can try and have a look into that. – LeftyX Dec 03 '15 at 13:12
  • I've already checked your sample repo. Thanks for your effort. I've exactly the same settings during startup and I had no luck to let it work in my case. The main difference between your sample and my scenario is I'm using OWIN Katana and HttpListener-based OWIN hosts use DPAPI instead of machine key. I'm getting crazy trying to fix this simple issue!!!!!!!!!!!!!!!!!!! – Matías Fidemraizer Dec 04 '15 at 08:44
  • Check my update 2 on my question. If I issue a request with the same access token to the Identity API, it works perfectly... I believe that there's something about how OWIN Katana hosts so the DPAPI detects that who emitted the token isn't the same that's receiving it to authorize requests... – Matías Fidemraizer Dec 04 '15 at 09:42
  • The X-Files Episode have has resolved.... It was nothing with configuration at all, or well, it has "a little" to do with it.... Actually it's the default DPAPI access token formatter/protector the responsible of not allowing an OAuth access token to be reusable across many services!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!! – Matías Fidemraizer Dec 04 '15 at 09:55
  • Glad you managed to fix it. Cheers. – LeftyX Dec 04 '15 at 09:56
  • Finally, I solved using this project: https://github.com/i4004/Owin.Security.AesDataProtectorProvider – Matías Fidemraizer Dec 04 '15 at 09:56
  • I've also marked my own question as a duplicate of http://stackoverflow.com/questions/21805755/using-oauth-tickets-across-several-services. It would be great that you could vote to close it as duplicate of that Q&A too ;) – Matías Fidemraizer Dec 04 '15 at 09:57
  • Yes, BTW I believe that it's a good point that my issue remains public because others might fail at the same point ;) – Matías Fidemraizer Dec 04 '15 at 09:59
  • @MatíasFidemraizer DPAPI encryption is machine or CurrentUser dependent ! – Legends May 31 '16 at 17:05
  • @LeftyX can resource server, in my case web API, be configured to use owin to validate JWT bearer token issued by a 3rd party authorisation server, in my case Auth0 server? – Amir Chatrbahr Jul 28 '17 at 15:30
  • 1
    @AmirChatrbahr, yes they are docoupled. Your Web Api (Resource Server) need to configure the authorization server: `UseJwtBearerAuthentication`. You can find a sample [here](https://auth0.com/docs/quickstart/backend/webapi-owin#configuration). It shouldn't be too complicated. Cheers. – LeftyX Jul 28 '17 at 16:51
  • @LeftyX Cheers mate! great link, exactly what I was looking for – Amir Chatrbahr Jul 30 '17 at 02:00
  • @AmirChatrbahr: No worries. Glad I've helped. Cheers. – LeftyX Jul 31 '17 at 07:44