Linc Abela
Linc Abela

Reputation: 815

Web API Authorization with ClientId and ClientSecret

Im using OWIN/Katana middleware in my web api authorization.

The flow.

I am issuing acess_token and refresh_token to the requesting client.

The access_token has short-lived lifespan while the refresh_token has long expiry.

As usual, if the access_token expires, it will request for another access_token using the refresh_token.

Now, my question. Since my refresh_token has long lifespan, it looks like its defeats the purpose of the short-lived access_token.Let say if the refresh_token is compromised, the hacker can still get the access_token, right?

I looked at OAuth implementation of google and microsoft and seems like they have this additional parameter that you need to supply in addition to the refresh_token. And this are the client_id and the client_secret. Seems like it is generated when they sign in on the API's developer page.

Now, How can I implement it in my project? Im thinking of overriding the token creation and make the token hash base on the ClientId and the ClientSecret.

I'm using the basic OWIN/Katana authentication of the latest web api and I don't plan to use other Authorization Server like Thinktecture. I just want to use the basic one that is provided by default by ASP.NET Web API 2

Startup.OAuth.cs

public partial class Startup
{
   static Startup()
   {
      PublicClientId = "self";
      UserManagerFactory = () => new UserManager<IdentityUser>(new AppUserStore());
      var tokenExpiry = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["ApiTokenExpiry"]);

      OAuthOptions = new OAuthAuthorizationServerOptions
      {
          TokenEndpointPath = new PathString("/Token"),
          Provider = new ApplicationOAuthProvider(PublicClientId, UserManagerFactory),
          AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
          AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(tokenExpiry),
          AllowInsecureHttp = true,
          RefreshTokenProvider = new AuthenticationTokenProvider
          {
               OnCreate = CreateRefreshToken,
               OnReceive = ReceiveRefreshToken,
          }
      };
   }

   private static void CreateRefreshToken(AuthenticationTokenCreateContext context)
   {
       var tokenExpiry = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["ApiTokenExpiry"]);
       var refreshTokenExpiry = Convert.ToInt32(System.Configuration.ConfigurationManager.AppSettings["ApiRefreshTokenExpiry"]);

       var refreshTokenProperties = new AuthenticationProperties(context.Ticket.Properties.Dictionary)
       {
           IssuedUtc = context.Ticket.Properties.IssuedUtc,
           ExpiresUtc = DateTime.UtcNow.AddMinutes(tokenExpiry + refreshTokenExpiry) // add 3 minutes to the access token expiry
       };

       var refreshTokenTicket = new AuthenticationTicket(context.Ticket.Identity, refreshTokenProperties);

       OAuthOptions.RefreshTokenFormat.Protect(refreshTokenTicket);
       context.SetToken(context.SerializeTicket());
   }

   private static void ReceiveRefreshToken(AuthenticationTokenReceiveContext context)
   {
       context.DeserializeTicket(context.Token);
   }
}

ApplicationOAuthProvider.cs

public class ApplicationOAuthProvider : OAuthAuthorizationServerProvider
{
    private readonly string _publicClientId;
    private readonly Func<UserManager<IdentityUser>> _userManagerFactory;

    public ApplicationOAuthProvider(string publicClientId, Func<UserManager<IdentityUser>> userManagerFactory)
    {
        if (publicClientId == null)
        {
            throw new ArgumentNullException("publicClientId");
        }

        if (userManagerFactory == null)
        {
            throw new ArgumentNullException("userManagerFactory");
        }

        _publicClientId = publicClientId;
        _userManagerFactory = userManagerFactory;
    }

    public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
         using (UserManager<IdentityUser> userManager = _userManagerFactory())
         {
             IdentityUser user = await userManager.FindAsync(context.UserName, context.Password);

             if (user == null)
             {
                 context.SetError("invalid_grant", "The user name or password is incorrect.");
                 return;
             }

             ClaimsIdentity oAuthIdentity = await userManager.CreateIdentityAsync(user,
                    context.Options.AuthenticationType);
             ClaimsIdentity cookiesIdentity = await userManager.CreateIdentityAsync(user,
                    CookieAuthenticationDefaults.AuthenticationType);
             AuthenticationProperties properties = CreateProperties(user.UserName);
             AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);

             context.Validated(ticket);
             context.Request.Context.Authentication.SignIn(cookiesIdentity);
         }
    }
}

Upvotes: 8

Views: 13009

Answers (1)

Frank Ibem
Frank Ibem

Reputation: 834

Summary --

  1. Navigate to https://console.developers.google.com/
  2. Select a project. If you don't have one, create one.
  3. On the left side bar under API'S and AUTH, select CREDENTIALS
  4. You should see your clientID and clientSecret. If you don't see these, click on create new client ID and complete that. Your clientID and clientSecret should then be displayed.

Refer to this link if you are still unsure: https://developers.google.com/accounts/docs/OAuth2Login#getcredentials

It describes how to obtain both the clientID and clientSecret.

Upvotes: -2

Related Questions