How can I map 'scope' values to Identity Claims? - asp.net-mvc

I've specified an Authorization Policy that requires the scope my_custom_value, e.g.
services.AddAuthorization(AuthConfig.GetAuthorizationOptions);
// ...
public static void GetAuthorizationOptions(AuthorizationOptions options)
{
options.AddPolicy("MyPolicy", policy =>
{
policy.RequireScope("my_custom_value");
});
Requests for endpoints that are protected by MyPolicy are failing because the Principal doesn't contain any scopes
I can see that my auth token has the following scopes:
"scope": [
"openid",
"profile",
"my_custom_value",
"offline_access"
],
It appears these are not being mapped to the Principal's claims. When I inspect the Claims later when the user attempts to access a protected endpoint, there are no scopes.
policy.RequireAssertion(context =>
{
if (context.User.HasClaim(c => c.Type == "scope")) // <-- always false
{
if (context.User.HasClaim(c => c.Value == "my_custom_value"))
{
return true;
}
}
Why are the scopes not being mapped? What do I need to do to map them?
For reference, I've tried it with
options.ClaimActions.MapUniqueJsonKey(JwtClaimTypes.Scope, "scope");
options.Scope.Add("my_custom_value");
Am I supposed to implement a custom IProfileService to include the scopes in the OnUserInformationReceived event?

When doing oidc auth using MVC only the IdentityToken claims are mapped to the ClaimsPrincipal. I couldn't figure out a way to map or include access tokens claims to the ClaimsPrincipal.
I ended up writing an authorization handler that validates the access token and performs required claim checks. I assume you read about authorization policies in asp.net 5.0.
public class AccessTokenAuthorizationHandler : AuthorizationHandler<AccessTokenRequirement> {
readonly IOptionsMonitor<OpenIdConnectOptions> _openIdConnectOptions;
readonly ILogger<AccessTokenAuthorizationHandler> _logger;
readonly IOptions<OpenIdOptions> _openIdOptions;
public AccessTokenAuthorizationHandler(
ILogger<AccessTokenAuthorizationHandler> logger,
IOptionsMonitor<OpenIdConnectOptions> openIdConnectOptions,
IOptions<OpenIdOptions> openIdOptions) {
_logger = logger;
_openIdConnectOptions = openIdConnectOptions;
_openIdOptions = openIdOptions;
}
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, AccessTokenRequirement requirement) {
if (context == null) {
throw new ArgumentNullException(nameof(context));
}
if (requirement == null) {
throw new ArgumentNullException(nameof(requirement));
}
if (context.Resource is Microsoft.AspNetCore.Mvc.ActionContext actionContext) {
ClaimsPrincipal principal = await GetAccessTokenPrincipal(actionContext.HttpContext).ConfigureAwait(false);
// verify your requirement
if (condition met) {
context.Succeed(requirement);
}
}
}
private async Task<ClaimsPrincipal> GetAccessTokenPrincipal(HttpContext httpContext) {
if (httpContext == null) {
return null;
}
String accessToken = await httpContext.GetUserAccessTokenAsync().ConfigureAwait(false);
if (!String.IsNullOrWhiteSpace(accessToken)) {
try {
TokenValidationParameters validationParameters = await BuildValidationParameters();
return new JwtSecurityTokenHandler().ValidateToken(accessToken, validationParameters, out var rawValidatedToken);
}
catch (SecurityTokenValidationException validationException) {
_logger.LogWarning(validationException, "Access token not valid.");
}
catch (Exception ex) {
_logger.LogError(ex, "Access token could not be validated.");
}
}
return null;
}
private async Task<TokenValidationParameters> BuildValidationParameters() {
var options = _openIdConnectOptions.Get(OpenIdConnectDefaults.AuthenticationScheme);
var discoveryDocument = await options.ConfigurationManager.GetConfigurationAsync(CancellationToken.None);
var signingKeys = discoveryDocument.SigningKeys;
var validationParameters = new TokenValidationParameters {
RequireExpirationTime = true,
RequireSignedTokens = true,
ValidateIssuer = true,
ValidIssuer = options.Authority,
ValidateIssuerSigningKey = true,
IssuerSigningKeys = signingKeys,
ValidateLifetime = true,
ValidateAudience = true,
ValidAudience = "your audience",
ValidateActor = false,
ValidTypes = new String[] { "at+jwt" },
ClockSkew = TimeSpan.FromMinutes(2),
};
return validationParameters;
}
}
I am not happy i had to do it this way, though i think it is done properly. To retrieve the access token i am using nuget package IdentityModel.AspNetCore, Version=3.0.0.0
I don't understand why not more people have this problem. Of course if your app consumes data from an api you pass on the access token, and there the access token becomes the claims principal. But if your mvc app performs direct database access (and might be later extracted to an api) you need to somehow be able to check claims of the access token. Maybe we have some conceptual misunderstanding...
Regarding the profile service. I think trying to include access token claims into the identity token would not be the correct approach. I think it wouldn't even be possible because you have no information about requested scopes when the service is called for the identity token.

Related

OpenID Connect: How to add custom claims data in the client credential flow

I'm setting up a client credential flow with my identity server to get an access token from a client. I'm able to get the access token with the following code,
Identity server configuration:
public void Configuration(IAppBuilder app)
{
app.Map("/identity", idsrvApp =>
{
var corsPolicyService = new DefaultCorsPolicyService()
{
AllowAll = true
};
var idServerServiceFactory = new IdentityServerServiceFactory()
.UseInMemoryClients(Clients.Get())
.UseInMemoryScopes(Scopes.Get())
.UseInMemoryUsers(Users.Get());
var options = new IdentityServerOptions
{
Factory = idServerServiceFactory,
SiteName = "Demo",
IssuerUri = IdentityConstants.IssuerUri,
PublicOrigin = IdentityConstants.STSOrigin,
SigningCertificate = LoadCertificate()
};
idsrvApp.UseIdentityServer(options);
});
}
Identity Server - Client configuration:
public static class Clients
{
public static IEnumerable<Client> Get()
{
return new[]
{
new Client
{
ClientId = "ClientSDK",
ClientName = "Client SDK (Client Credentials)",
Flow = Flows.ClientCredentials,
AllowAccessToAllScopes = true,
ClientSecrets = new List<Secret>()
{
new Secret(IdentityConstants.ClientSecret.Sha256())
}
}
};
}
}
MVC Client:
var oAuth2Client = new TokenClient(
IdentityConstants.STSTokenEndpoint,
"ClientSDK",
IdentityConstants.ClientSecret);
var tokenResponse = oAuth2Client.RequestClientCredentialsAsync("MyScope").Result;
return tokenResponse.AccessToken;
I'm able to get the access token(i.e. JWT). Can one please tell me how to add a unique key like (UserId) from my database, when the JWT is created with its claims data when the token is created.
First, you need to create custom attribute "userId" on Azure Portal, and apply it for selected application. Then follow this example,
Update user using Graph API
If you are using built in user flows, then you need to select "userId" for your application.
If you are using custom policy, then following process.
JWT token shows only output claims of Azure AD B2C custom policy. It is a multi steps process to create and update custom policy. Here is link to read more about How to create custom attribute
You should implement custom user store for validating user and adding claims from database. Change startup code like below, Userrepository class represents database communication to authenticate user and get claims from database:
var idServerServiceFactory = new IdentityServerServiceFactory()
.UseInMemoryClients(Clients.Get())
.UseInMemoryScopes(Scopes.Get())
.AddCustomUserStore();
Add below classes and change according to your requirement:
public static class CustomIdentityServerBuilderExtensions
{
public static IIdentityServerBuilder AddCustomUserStore(this IIdentityServerBuilder builder)
{
builder.AddProfileService<UserProfileService>();
builder.AddResourceOwnerValidator<UserResourceOwnerPasswordValidator>();
return builder;
}
}
public class UserProfileService : IProfileService
{
public async Task GetProfileDataAsync(ProfileDataRequestContext context)
{
UserRepository userRepository=new UserRepository();
var user = userRepository.GetUserById(int.Parse(context.Subject.GetSubjectId()));
if (user != null)
{
var userTokenModel = _mapper.Map<UserTokenModel>(user);
var claims = new List<Claim>();
claims.Add(new Claim("UserId", user.UserId));
// Add another claims here
context.IssuedClaims.AddRange(claims);
}
public async Task IsActiveAsync(IsActiveContext context)
{
}
}
public class UserResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
{
public async Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
UserRepository userRepository=new UserRepository();
var userLoginStatus = userRepository.GetUserById(context.UserName, context.Password);
if (userLoginStatus != null)
{
context.Result = new GrantValidationResult(userLoginStatus.UserId.ToString(),
OidcConstants.AuthenticationMethods.Password);
}
else
{
context.Result = new GrantValidationResult(TokenRequestErrors.InvalidClient,
"Wrong Credentials");
}
}
}

JWT Token authentication - Doing it right way

Overview of my project structure:
I have 2 Projects.
Asp.net core Web Api
Asp.net core Web MVC
In Web Api Project
I am NOT using Asp.net core Identity for login, instead, I am using my own login mechanism.
LoginAction method will authenticate user in database and generate JWT Token.
I was able to generate JWT Token and Life is smooth till this point.
Generate Token
[AllowAnonymous]
[Route("requesttoken")]
[HttpPost]
public async Task<IActionResult> RequestToken([FromBody] TokenRequest request)
{
var result = await IsValidUser(request);
if(result)
{
var claims = new[]
{
new Claim(ClaimTypes.Name, request.Email)
};
var key = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(_myAppSettings.SecurityKey));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: _myAppSettings.WebsiteName.ToLower(),
audience: _myAppSettings.WebsiteName.ToLower(),
claims: claims,
notBefore: Utilities.GetEST_DateTimeNow(),
expires: Utilities.GetEST_DateTimeNow().AddMinutes(5),
signingCredentials: creds);
return Ok(new
{
token = new JwtSecurityTokenHandler().WriteToken(token)
});
}
else
{
return Unauthorized();
}
}
Inside Startup class
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.Configure<MyAppSettings>(Configuration.GetSection("MyAppSettings"));
#region Validate JWT Token
ConfigureJwtAuthService(services, Configuration);
#endregion
services.AddMvc();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseAuthentication();
app.UseMvc();
}
JWT Validation part (As partial startup class)
public void ConfigureJwtAuthService(IServiceCollection services, IConfiguration configuration)
{
var symmetricKeyAsBase64 = configuration["MyAppSettings:SecurityKey"];
var keyByteArray = Encoding.ASCII.GetBytes(symmetricKeyAsBase64);
var signingKey = new SymmetricSecurityKey(keyByteArray);
var tokenValidationParameters = new TokenValidationParameters
{
// The signing key must match!
ValidateIssuerSigningKey = true,
IssuerSigningKey = signingKey,
// Validate the JWT Issuer (iss) claim
ValidateIssuer = true,
ValidIssuer = Configuration["MyAppSettings:WebsiteName"].ToLower(),
// Validate the JWT Audience (aud) claim
ValidateAudience = true,
ValidAudience = Configuration["MyAppSettings:WebsiteName"].ToLower(),
// Validate the token expiry
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
services.AddAuthentication(
options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(o => o.TokenValidationParameters = tokenValidationParameters);
}
Sample response of LoginAction Method.
{
"token": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkrDtGhuIETDs8OoIiwiYWRtaW4iOnRydWV9.469tBeJmYLERjlKi9u6gylb-2NsjHLC_6kZNdtoOGsA"
}
In Web MVC Project
I am consuming above Web Api and passing login parameters and was able to get JWT Token response.
I am storing JWT Token response in cookie [Manually - _httpContextAccessor.HttpContext.Response.Cookies.Append(key, jwtTokenValue, option);]
Based on JWT Token response receive, I am trying to extract claims from that JWT Token, so that I can able to create valid identity of user and login user on the web.
I am trying to achieve something like below:
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, model.Email)
};
var userIdentity = new ClaimsIdentity(claims, "login");
ClaimsPrincipal principal = new ClaimsPrincipal(userIdentity);
await HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal);
return RedirectToLocal(returnUrl);
}
Questions
Am I doing right by storing JWT Token in cookie. Is my manual approach of storing cookie is correct or is there any better way?
How can I get claims from JWT in Web Project, so that I can able to singin user using cookie?
Want to do it right way, any help would be much appreciated.
Following seems to helped me: http://blogs.quovantis.com/json-web-token-jwt-with-web-api/ not sure whether that is right way of doing or not.
/// Using the same key used for signing token, user payload is generated back
public JwtSecurityToken GenerateUserClaimFromJWT(string authToken)
{
var tokenValidationParameters = new TokenValidationParameters()
{
ValidAudiences = new string[]
{
"http://www.example.com",
},
ValidIssuers = new string[]
{
"self",
},
IssuerSigningKey = signingKey
};
var tokenHandler = new JwtSecurityTokenHandler();
SecurityToken validatedToken;
try {
tokenHandler.ValidateToken(authToken,tokenValidationParameters, out validatedToken);
}
catch (Exception)
{
return null;
}
return validatedToken as JwtSecurityToken;
}

JWT token isn't validated even if it's correct using Microsoft.Azure.Mobile SDK

I'm using Microsoft.Azure.Mobile SDK for implementing Server code.
Code in OWIN startup is as below:
public void ConfigureAuth(IAppBuilder app)
{
HttpConfiguration config = new HttpConfiguration();
new MobileAppConfiguration().ApplyTo(config);
app.UseAppServiceAuthentication(new AppServiceAuthenticationOptions
{
SigningKey = ConfigurationManager.AppSettings["SigningKey"],
ValidAudiences = new[] { ConfigurationManager.AppSettings["ValidAudience"] },
ValidIssuers = new[] { ConfigurationManager.AppSettings["ValidIssuer"] },
TokenHandler = config.GetAppServiceTokenHandler()
});
app.UseWebApi(config);
}
Token generation code:
Claim[] claims = new Claim[]
{
new Claim("sub", "SampleSubject"),
new Claim("Id", Convert.ToString(Users[0].user_id)),
new Claim("name", Users[0].name),
new Claim("surname", Users[0].surname),
new Claim(ClaimTypes.Role, "user")
};
var token = AppServiceLoginHandler.CreateToken(claims, ConfigurationManager.AppSettings["SigningKey"], ConfigurationManager.AppSettings["ValidAudience"], ConfigurationManager.AppSettings["ValidIssuer"], TimeSpan.FromDays(30));
return token.RawData;
A sample JWT token is
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJzdWIiOiJHcnViZXJBUEkiLCJJZCI6IjMyODkwIiwibmFtZSI6IkRhdmlkZSIsInN1cm5hbWUiOiJCb25ldHRhIiwicm9sZSI6InVzZXIiLCJ2ZXIiOiIzIiwiaXNzIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MjM1MzEvIiwiYXVkIjoiaHR0cHM6Ly9sb2NhbGhvc3Q6MjM1MzIvIiwiZXhwIjoxNTAyNDYyNDQzLCJuYmYiOjE0OTk4NzA0NDN9.b5VhWzvkaEumutPZpLzImcAy4NotXCSgUIqLltVUQWI
The token is valid per below screenshot
And for below code,
[Authorize]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
// GET api/values/5
public string Get(int id)
{
try
{
ClaimsPrincipal claims;
AppServiceTokenHandler s = new AppServiceTokenHandler(new HttpConfiguration());
s.TryValidateLoginToken(Request.Headers.Authorization.Parameter, ConfigurationManager.AppSettings["SigningKey"], new[] { ConfigurationManager.AppSettings["ValidAudience"] }, new[] { ConfigurationManager.AppSettings["ValidIssuer"] }, out claims);
AppServiceTokenHandler.ValidateToken(Request.Headers.Authorization.Parameter, ConfigurationManager.AppSettings["SigningKey"], ConfigurationManager.AppSettings["ValidAudience"], ConfigurationManager.AppSettings["ValidIssuer"]);
}
catch (Exception ex)
{
throw;
}
return "value";
}
'/Get' request fails with HTTP 401.
BUT For the same JWT token 'Get/5' returns HTTP 200 (validates token manually).
The problem is, when I use Authorize attribute, api returns 401.
According to your description, I checked this issue on my side.
Access the protected Web API
In summary, you could refer to the above screen shoot and check with your api endpoint. Additionally, you could refer to AppServiceTokenHandler.cs and HmacSigningCredentials.cs for implementing your custom TokenHandler to troubleshoot this issue. Moreover, you could refer to adrian hall's book about Custom Authentication
.

OWIN Authorization Server and External Signin with Google/Facebook

It's been long since I had a question.
Ok, so, I read this article and downloaded the sample code. Very nice examples of using OWIN to create an Authorization Server with Google signin included. It also includes 4 clients for Authorization Code Grant, Client Credential Grant, Implicit Grant and Resource Owner Password Credential Grant.
for sake of brevity I am going to post here just the configuration Startup file, then my problem and questions. Also, I found my problem using the Implicit Grant client. Here we go...
Startup.cs
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
// Enable Application Sign In Cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "Application",
AuthenticationMode = AuthenticationMode.Passive,
LoginPath = new PathString(Paths.LoginPath),
LogoutPath = new PathString(Paths.LogoutPath),
});
// Enable External Sign In Cookie
app.SetDefaultSignInAsAuthenticationType("External");
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = "External",
AuthenticationMode = AuthenticationMode.Passive,
CookieName = CookieAuthenticationDefaults.CookiePrefix + "External",
ExpireTimeSpan = TimeSpan.FromMinutes(2),
});
// Enable google authentication
//var googleOptions = new GoogleOAuth2AuthenticationOptions
//{
// Caption = "Google+",
// ClientId = "blablabla.apps.googleusercontent.com",
// ClientSecret = "Q1zNmqf-U3ZffeZgcTPh760j",
// CallbackPath = new PathString("/OAuth/Authorize"),
// Provider = new GoogleOAuth2AuthenticationProvider
// {
// OnAuthenticated = async context =>
// {
// context.Identity.AddClaim(new Claim(ClaimTypes.Name, context.Identity.FindFirst(ClaimTypes.Name).Value));
// context.Identity.AddClaim(new Claim(ClaimTypes.Name, context.Identity.FindFirst(ClaimTypes.Email).Value));
// context.Identity.AddClaim(new Claim(ClaimTypes.Name, context.User.GetValue("picture").ToString()));
// context.Identity.AddClaim(new Claim(ClaimTypes.Name, context.User.GetValue("profile").ToString()));
// context.Identity.AddClaim(new Claim("Token", context.AccessToken));
// }
// }
//};
//googleOptions.Scope.Add("https://www.googleapis.com/auth/plus.login");
//googleOptions.Scope.Add("https://www.googleapis.com/auth/plus.login");
//app.UseGoogleAuthentication(googleOptions);
app.UseGoogleAuthentication();
// Setup Authorization Server
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
AuthorizeEndpointPath = new PathString(Paths.AuthorizePath),
TokenEndpointPath = new PathString(Paths.TokenPath),
ApplicationCanDisplayErrors = true,
AllowInsecureHttp = true,
// Authorization server provider which controls the lifecycle of Authorization Server
Provider = new OAuthAuthorizationServerProvider
{
OnValidateClientRedirectUri = ValidateClientRedirectUri,
OnValidateClientAuthentication = ValidateClientAuthentication,
OnGrantResourceOwnerCredentials = GrantResourceOwnerCredentials,
OnGrantClientCredentials = GrantClientCredetails
},
// Authorization code provider which creates and receives authorization code
AuthorizationCodeProvider = new AuthenticationTokenProvider
{
OnCreate = CreateAuthenticationCode,
OnReceive = ReceiveAuthenticationCode,
},
// Refresh token provider which creates and receives referesh token
RefreshTokenProvider = new AuthenticationTokenProvider
{
OnCreate = CreateRefreshToken,
OnReceive = ReceiveRefreshToken,
}
});
}
private Task ValidateClientRedirectUri(OAuthValidateClientRedirectUriContext context)
{
if (context.ClientId == Clients.Client1.Id)
{
context.Validated(Clients.Client1.RedirectUrl);
}
else if (context.ClientId == Clients.Client2.Id)
{
context.Validated(Clients.Client2.RedirectUrl);
}
return Task.FromResult(0);
}
private Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
string clientId;
string clientSecret;
if (context.TryGetBasicCredentials(out clientId, out clientSecret) ||
context.TryGetFormCredentials(out clientId, out clientSecret))
{
if (clientId == Clients.Client1.Id && clientSecret == Clients.Client1.Secret)
{
context.Validated();
}
else if (clientId == Clients.Client2.Id && clientSecret == Clients.Client2.Secret)
{
context.Validated();
}
}
return Task.FromResult(0);
}
private Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
var identity = new ClaimsIdentity(new GenericIdentity(context.UserName, OAuthDefaults.AuthenticationType), context.Scope.Select(x => new Claim("urn:oauth:scope", x)));
context.Validated(identity);
return Task.FromResult(0);
}
private Task GrantClientCredetails(OAuthGrantClientCredentialsContext context)
{
var identity = new ClaimsIdentity(new GenericIdentity(context.ClientId, OAuthDefaults.AuthenticationType), context.Scope.Select(x => new Claim("urn:oauth:scope", x)));
context.Validated(identity);
return Task.FromResult(0);
}
private readonly ConcurrentDictionary<string, string> _authenticationCodes =
new ConcurrentDictionary<string, string>(StringComparer.Ordinal);
private void CreateAuthenticationCode(AuthenticationTokenCreateContext context)
{
context.SetToken(Guid.NewGuid().ToString("n") + Guid.NewGuid().ToString("n"));
_authenticationCodes[context.Token] = context.SerializeTicket();
}
private void ReceiveAuthenticationCode(AuthenticationTokenReceiveContext context)
{
string value;
if (_authenticationCodes.TryRemove(context.Token, out value))
{
context.DeserializeTicket(value);
}
}
private void CreateRefreshToken(AuthenticationTokenCreateContext context)
{
context.SetToken(context.SerializeTicket());
}
private void ReceiveRefreshToken(AuthenticationTokenReceiveContext context)
{
context.DeserializeTicket(context.Token);
}
}
Now, notice that there is a piece of code that has been commented out. Here is the key and confusion.
The original code comes just like this app.UseGoogleAuthentication(); So, this code works perfect from the get go (and for those who want to download and test this code, it's ready to go, just hit F5).
Now, I understand that this code so far, all is doing is allowing someone "authenticate" using Google just to get a "validated user" response, then creating its own validation/token (LOCAL AUTHORITY).
The problem comes when I want to extend a little bit more that google authentication. I want to actually gather more data from Google (other than just a valid/not valid google user). As soon as I uncomment those lines, the authorization part of the Authorization Server stops. I don't get an exception or anything, it just stays spinning.
Has anyone had experience on this specific issue before?
Thanks!

How to do long lived tokens with Katana OAuth Bearer Tokens

From the SPA template i managed to get basic OAuth flows working.
OAuthOptions = new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
ApplicationCanDisplayErrors = true,
TokenEndpointPath = new Microsoft.Owin.PathString("/Token"),
AuthorizeEndpointPath = new Microsoft.Owin.PathString("/api/Account/ExternalLogin"),
Provider = new CompositeWebroleOauthProvider<User>(PublicClientId, IdentityManagerFactory, CookieOptions)
};
I have a single page application that is hosted on a seperate domain that will interact with the webapi using the bearer tokens from the Token endpoint.
I am doing the ResourceOwnerCredentials flow, with a request with the following data:
data: {
grant_type: "password",
username: username,
password: password
}
These tokens are short lived ect. I now would like to extend my application such I can get a refress token or something such I do not have to authenticate all the time.
What is my next steps?
The GrantResourceOwnerCredentials implementation:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
using (var identityManager = _identityManagerFactory.Create())
{
var user = await identityManager.FindAsync(context.UserName, context.Password);
if (user == null)
{
context.SetError("invalid_grant", "The user name or password is incorrect.");
return;
}
ClaimsIdentity oAuthIdentity = await identityManager.CreateIdentityAsync(user, context.Options.AuthenticationType);
AuthenticationProperties properties = CreatePropertiesAsync(user);
AuthenticationTicket ticket = new AuthenticationTicket(oAuthIdentity, properties);
context.Validated(ticket);
}
}
I just had to set the provider for it to generate refresh tokens.
Any comments for pointers on when to set refresh tokens and not would be nice.
RefreshTokenProvider = new AuthenticationTokenProvider
{
OnCreate = CreateRefreshToken,
OnReceive = ReceiveRefreshToken,
}
private void CreateRefreshToken(AuthenticationTokenCreateContext context)
{
context.SetToken(context.SerializeTicket());
}
private void ReceiveRefreshToken(AuthenticationTokenReceiveContext context)
{
context.DeserializeTicket(context.Token);
}

Resources