Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can two different OWIN applications authorize an OAuth2 bearer access token? [duplicate]

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...).

like image 845
Matías Fidemraizer Avatar asked Dec 01 '15 13:12

Matías Fidemraizer


People also ask

Can you have multiple access tokens?

Access tokens are generated for an application, not a user, but yes, there can be multiple access tokens authorized by a single user - the user authorizes the application to perform some operations (scopes) on his behalf.

Is bearer token same as OAuth2?

Bearer tokens are for OAuth2 authentication. A bearer token is an encoded value that generally contains the user ID, authenticated token and a timetamp. It is most commonly used in REST APIs. If the API supports OAuth2 then it'll use a bearer token.

What is the difference between bearer token and access token?

Using a bearer token does not require a bearer to prove possession of cryptographic key material (proof-of-possession). Access tokens are used in token-based authentication to allow an application to access an API.

How do I authorize a bearer token?

Bearer token Bearer tokens enable requests to authenticate using an access key, such as a JSON Web Token (JWT). The token is a text string, included in the request header. In the request Authorization tab, select Bearer Token from the Type dropdown list. In the Token field, enter your API key value.


1 Answers

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.

like image 161
LeftyX Avatar answered Sep 22 '22 22:09

LeftyX