2

I am using Asp.net Webform project with Web API. I configured JWT token-based authentication and now I want to customize the authentication response

Here are my configurations,

  1. Startup.cs

    public class Startup
    {
         public void Configuration(IAppBuilder app)
         {
             HttpConfiguration config = new HttpConfiguration();
    
             // Web API routes
             config.MapHttpAttributeRoutes();
    
             app.UseCors(Microsoft.Owin.Cors.CorsOptions.AllowAll);
    
             ConfigureOAuth(app);
    
             app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
    
             WebApiConfig.Register(config);
    
         }
    
         public void ConfigureOAuth(IAppBuilder app)
         {
    
             String apiHttpOnly = ConfigurationManager.AppSettings["AllowInsecureHttp"];
             String tokenTimeSpan = ConfigurationManager.AppSettings["tokenTimeSpanFromMinutes"];
    
             bool allowInsecureHttp = !String.IsNullOrEmpty(apiHttpOnly) ? 
                                      Convert.ToBoolean(apiHttpOnly) : false;
             int accessTokenExpireTimeSpan = !String.IsNullOrEmpty(tokenTimeSpan) ? 
                                      Convert.ToInt32(tokenTimeSpan) : 60;
    
             var authProvider = new AuthorizationServiceProvider();
             OAuthAuthorizationServerOptions options = new OAuthAuthorizationServerOptions
             {
                 //For Dev enviroment only (on production should be AllowInsecureHttp = false)
                 AllowInsecureHttp = allowInsecureHttp,
                 TokenEndpointPath = new PathString("/api/authenticate"),
                 AccessTokenExpireTimeSpan = TimeSpan.FromDays(accessTokenExpireTimeSpan),
                 Provider = authProvider
             };
    
             app.UseOAuthAuthorizationServer(options);
    
         }
    }
    
  2. AuthorizationServiceProvider

     public class AuthorizationServiceProvider : OAuthAuthorizationServerProvider
     {
         public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
         {
             context.Validated();
             return base.ValidateClientAuthentication(context);
         }
    
         public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
         {
             var identity = new ClaimsIdentity(context.Options.AuthenticationType);
    
             if (Membership.ValidateUser(context.UserName, context.Password))
             {
                 identity.AddClaim(new Claim(ClaimTypes.Role, "admin"));
                 identity.AddClaim(new Claim("username", context.UserName));
                 identity.AddClaim(new Claim(ClaimTypes.Name, "admin admin"));
                 context.Validated(identity);
             }
             else
             {
                 context.SetError("invalid_grant", "Provide username and password is incorrect.");
    
             }
    
             return base.GrantResourceOwnerCredentials(context);
         }
     }
    

When I call the API with the right credentials, it returns like

{
    "access_token": "uEwmXl6N0mJXVUZesxA_2tG5lIuZUIUDaxtjAl0QGE6j2-J7n4c63zboOUClGjRQf1IDY9-nBgyq0HP5WR7MMxTYoHGIyiHIbcKu9AYwhECCGaVBCxY2Ounhit4N1pYK1vV6uX6AcoA-a0xhytF8Jz27D77ZvCLi3PuUQDEXSp0pkGG796wu1fRZCaRsCB-kLoa-_V7KJaGGhhoybN_c0GNOBhhwmGpx6Js26-Vx-lmWpfsPUE1aKrJfx-oMcyE5x7CooAlx4vA6iZhnNfmYdRejRKoKKnObyuAsym7mVdZY3bpv",
    "token_type": "bearer",
    "expires_in": 5183999
}

I want to customize the response by adding some extra attributes like,

{
    "access_token": "uEwmXl6N0mJXVUZesxA_2tG5lIuZUIUDaxtjAl0QGE6j2-J7n4c63zboOUClGjRQf1IDY9-nBgyq0HP5WR7MMxTYoHGIyiHIbcKu9AYwhECCGaVBCxY2Ounhit4N1pYK1vV6uX6AcoA-a0xhytF8Jz27D77ZvCLi3PuUQDEXSp0pkGG796wu1fRZCaRsCB-kLoa-_V7KJaGGhhoybN_c0GNOBhhwmGpx6Js26-Vx-lmWpfsPUE1aKrJfx-oMcyE5x7CooAlx4vA6iZhnNfmYdRejRKoKKnObyuAsym7mVdZY3bpv",
    "token_type": "bearer",
    "expires_in": 5183999,
    "attribute1" : "abc",
    "attribute2" : "ert"
}

Anyone have an idea to do that?

shalitha senanayaka
  • 1,905
  • 20
  • 37

1 Answers1

0

Finally, I investigate the problem and find out the solution. I posted it here, maybe it may help someone.

  1. Add AuthenticationProperties

    var props = new AuthenticationProperties(new Dictionary<string, string>
    {
        {
             "attribute1" , "abc"
        },
        {
             "attribute2" , "ert"
        }
    });
    
  2. Then create Ticket using AuthenticationTicket

    var ticket = new AuthenticationTicket(identity, props);
    
  3. Add ticket to the context

    context.Validated(ticket);
    
  4. Then implement the override method TokenEndpoint

     public override Task TokenEndpoint(OAuthTokenEndpointContext context)
     {
         foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
         {
             context.AdditionalResponseParameters.Add(property.Key, property.Value);
         }
         return Task.FromResult<object>(null);
     }
    

Finally AuthorizationServiceProvider class be like,

public class AuthorizationServiceProvider : OAuthAuthorizationServerProvider
{
    public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
    {
        context.Validated();
        return base.ValidateClientAuthentication(context);
    }

    public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
    {
        var identity = new ClaimsIdentity(context.Options.AuthenticationType);

        if (string.IsNullOrEmpty(context.UserName) || string.IsNullOrEmpty(context.Password))
        {
            context.SetError("invalid_request", "No username or password are provided.");
        }
        else if (Membership.ValidateUser(context.UserName, context.Password))
        {
            
            identity.AddClaim(new Claim("username", context.UserName));
            identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));

            /*
             * All custom response props are define here.
             * --------usage----------
             * use as dictionary
             * */
            var props = new AuthenticationProperties(new Dictionary<string, string>
            {
                //{
                //    "test" , "val"
                //}
            });

            var ticket = new AuthenticationTicket(identity, props);


            context.Validated(ticket);
        }
        else
        {
            context.SetError("invalid_grant", "Provide username and password is incorrect.");

        }

        return base.GrantResourceOwnerCredentials(context);
    }

    public override Task TokenEndpoint(OAuthTokenEndpointContext context)
    {
        foreach (KeyValuePair<string, string> property in context.Properties.Dictionary)
        {
            context.AdditionalResponseParameters.Add(property.Key, property.Value);
        }
        return Task.FromResult<object>(null);
    }
}

For more info - https://stackoverflow.com/a/26369622/8403632

Sample Example - https://github.com/Leftyx/OwinWebApiBearerToken

shalitha senanayaka
  • 1,905
  • 20
  • 37