Manual set cookies using accesstoken/AuthenticationResult -like UseCookieAuthentication() owin middleware - c#

In an Action in a Controller, we have the AuthenticationResult which contains AccessToken.
We would like to do a manual login with this token that is equal to the UserCookieAuthentication owin middleware. Is that possible?
public partial class Startup
{
public void ConfigureAuth(IAppBuilder app)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseWindowsAzureActiveDirectoryBearerAuthentication(
new WindowsAzureActiveDirectoryBearerAuthenticationOptions
{
Tenant = ConfigurationManager.AppSettings["ida:Tenant"],
TokenValidationParameters = new TokenValidationParameters
{
ValidAudience = ConfigurationManager.AppSettings["ida:Audience"]
},
});
}
}
I have been looking at HttpContext.GetOwinContext().Authentication.SignIn() but that doesn't take any Tokens.

Looking in the Katana/Owin Github project the following TokenHelper solved it along with using the AuthenticationResponseGrant method:
result = await authContext.AcquireTokenAsync(resource, clientId, uc);
var principal = await new TokenHelper().GetValidatedClaimsPrincipalAsync(result.AccessToken);
var claimsIdentity = new ClaimsIdentity(principal.Claims, CookieAuthenticationDefaults.AuthenticationType);
var properties = new AuthenticationProperties();
HttpContext.GetOwinContext().Authentication.AuthenticationResponseGrant =
new AuthenticationResponseGrant(claimsIdentity, properties);

Related

Openiddict NET 4.8 ASP WebForm client, OWIN startup implementation

Implemented Server Client Authorization based on recent Velusia example taken from here: openiddict-samples
all ASP NET.CORE clients work fine, but unfortunately I have to implement token authorization flow for some old ASP NET 4.8 Web form applications.
Added following OWIN startup class
public class Startup
{
// These values are stored in Web.config. Make sure you update them!
private readonly string _clientId = ConfigurationManager.AppSettings["ClientId"];
private readonly string _redirectUri = ConfigurationManager.AppSettings["RedirectUri"];
private readonly string _authority = ConfigurationManager.AppSettings["AuthUrl"];
private readonly string _clientSecret = ConfigurationManager.AppSettings["ClientSecret"];
public void Configuration(IAppBuilder app)
{
ConfigureAuth(app);
}
public void ConfigureAuth(IAppBuilder app)
{
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
ClientId = _clientId,
ClientSecret = _clientSecret,
Authority = _authority,
RedirectUri = _redirectUri,
RequireHttpsMetadata = false,
ResponseType = OpenIdConnectResponseType.Code,
Scope = $"{OpenIdConnectScope.OpenId} profile {OpenIdConnectScope.Email} roles",
TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name" , RoleClaimType = "role"},
CallbackPath = new PathString("//"), //signin-oidc
Notifications = new OpenIdConnectAuthenticationNotifications
{
AuthorizationCodeReceived = async n =>
{
var client = new HttpClient();
var tokenResponse = await client.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest
{
Address = _authority + "/connect/token",
ClientId = _clientId,
ClientSecret = _clientSecret,
Code = n.Code,
RedirectUri = _redirectUri
});
if (!tokenResponse.IsError)
{
var claims = new List<Claim>()
{
new Claim("id_token", tokenResponse.IdentityToken),
new Claim("access_token", tokenResponse.AccessToken)
};
n.AuthenticationTicket.Identity.AddClaims(claims);
}
},
},
});
}
}
behind the login button following code:
if (!Request.IsAuthenticated)
{
HttpContext.Current.GetOwinContext().Authentication.Challenge(
new AuthenticationProperties { RedirectUri = "/" },
OpenIdConnectAuthenticationDefaults.AuthenticationType);
}
initially it starts fine, click on the login button and request goes to the server, I can see server login page. Once the password is entered request comes back to the client and opens following page: https://localhost/signin-oidc ... and I never get AuthorizationCodeReceived event triggered!
Anybody tried something similar?

How to extend cookie expiration CORRECTLY? Users got automatically kicked in less than an hour

I am using cookies to persist authenticated users on my page
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc(config =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
});
services.AddAuthentication(options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)
{
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AccessDeniedPath = "/Accounts/Forbidden/",
AuthenticationScheme = CookieAuthenticationDefaults.AuthenticationScheme,
AutomaticAuthenticate = true,
AutomaticChallenge = true,
LoginPath = "/Account/Login/",
LogoutPath = "/Account/Login/",
ExpiresTimeSpan = TimeSpan.FromDays(10)
});
}
AccountController.cs
public async Task<IActionController> Login(UserVm model)
{
if(_repo.ValidateCredentials(model.UserName, model.Password))
{
var user = _repo.FindUser(model.UserName);
AuthenticationProperties props = null;
if(model.RememberLogin)
{
props = new AuthenticationProperties
{
IsPersistent = true,
ExpiresUtc = DataTimeOffset.UtcNow.Add(TimeSpan.FromDays(10))
};
}
ClaimsIdentity identity = new ClaimsIdentity(GetUserClaims(user), CookieAuthenticationDefaults.AuthenticationScheme);
ClaimsPrincipal principal = new ClaimsPrincipal(identity);
await HttpContext.Authentication.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme, principal, props);
return Redirect("~/");
}
}
It says here, if I enable IsPersistent, it'll override the ExpireTimeSpanon CookieAuthenticationOptions So, I did. I want to extend my cookies to 10days. But it seems that my users got automatically kicked out in less than an hour. I noticed this when I published my application live on IIS. Is this something to do with my server?
How can I extend my cookies expiration CORRECTLY so my users won't be kicked out? I'm using netcoreapp1.1

AspNetCore 2.1 Bearer Token Authentication - Current user is empty

I've an application that requests a token when the user signs in.
That token is then passed with the following header:
Authorization: Bearer <TOKEN>
I've the following code on my startup.cs (aspnet core 2.1):
public void ConfigureServices(IServiceCollection services)
{
services.AddMvcCore()
.SetCompatibilityVersion(CompatibilityVersion.Latest)
.AddFormatterMappings()
.AddJsonFormatters()
.AddCors()
.AddAuthorization(o =>
{
o.DefaultPolicy = new AuthorizationPolicyBuilder(JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
});
/* Code... */
ConfigureAuthentication(services);
/* Code... */
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAuthentication()
.UseMiddleware<ExceptionMiddleware>(container)
.UseCors(x =>
{
x.WithOrigins("*")
.AllowAnyMethod()
.AllowCredentials()
.AllowAnyHeader()
.Build();
});
/* Code... */
}
private void ConfigureAuthentication(IServiceCollection services)
{
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
var tokenProvider = new HumbleTokenProvider(container);
options.TokenValidationParameters = tokenProvider.GetValidationParameters();
options.RequireHttpsMetadata = false;
});
}
To create tokens when the user sign in, I've TokenProvider service:
public class RsaJwtTokenProvider : ITokenProvider
{
readonly IConfiguration configuration;
readonly IDateFactory dateFactory;
readonly RsaSecurityKey _key;
readonly string _algorithm;
readonly string _issuer;
readonly string _audience;
public RsaJwtTokenProvider(
IConfiguration configuration,
IDateFactory dateFactory
)
{
this.configuration = configuration;
this.dateFactory = dateFactory;
var parameters = new CspParameters { KeyContainerName = configuration.GetSection("TokenAuthentication:SecretKey").Value };
var provider = new RSACryptoServiceProvider(2048, parameters);
_key = new RsaSecurityKey(provider);
_algorithm = SecurityAlgorithms.RsaSha256Signature;
_issuer = configuration.GetSection("TokenAuthentication:Issuer").Value;
_audience = configuration.GetSection("TokenAuthentication:Audience").Value;
}
public (string Token, int Expires) CreateToken(string userName, string UserId)
{
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();
var claims = new List<Claim>()
{
new Claim(ClaimTypes.NameIdentifier, UserId),
new Claim(ClaimTypes.Name, userName)
};
ClaimsIdentity identity = new ClaimsIdentity(claims, "jwt");
int expiresIn = int.Parse(configuration.GetSection("TokenAuthentication:Validaty").Value);
DateTime expires = dateFactory.Now.AddMinutes(expiresIn).ToUniversalTime();
SecurityToken token = tokenHandler.CreateJwtSecurityToken(new SecurityTokenDescriptor
{
Audience = _audience,
Issuer = _issuer,
SigningCredentials = new SigningCredentials(_key, _algorithm),
Expires = expires,
Subject = identity
});
return (tokenHandler.WriteToken(token), expiresIn);
}
public TokenValidationParameters GetValidationParameters()
{
return new TokenValidationParameters
{
// The signing key must match!
ValidateIssuerSigningKey = true,
IssuerSigningKey = _key,
// Validate the JWT Issuer (iss) claim
ValidateIssuer = true,
ValidIssuer = _issuer,
// Validate the JWT Audience (aud) claim
ValidateAudience = true,
ValidAudience = _audience,
// Validate the token expiry
ValidateLifetime = true,
// If you want to allow a certain amount of clock drift, set that here:
ClockSkew = TimeSpan.Zero
};
}
}
As you can see, TokenValidationParameters used in AddJwtBearer is provided by the code above GetValidationParameters.
My first perception on this, was that none of the startup authorization/authentication methods checked for the token, or at least I'm not providing it besides the TokenValidationParameters.
I assumed that it worked because of the Token composition and the service would decompose it to extract the current user and insert it into Identity.
However, when I call userManager.GetUserId(user) it returns null.
public string CurrentUser
{
get
{
var user = accessor.HttpContext?.User;
if (user != null)
return userManager.GetUserId(user);
return null;
}
}
The content of user is the following:
What am I doing wrong?
Screenshot Claims (Token creation)
Update
With the help of Mohammed Noureldin I've discovered that I didn't have claims in my CurrentUser property.
After putting [Authorize] in my controller it started working.
However, I need it to work on anonymous actions too...
Any idea?
If I understood your problem correctly, you are not able to get who is the current logged in User from the Identity.
You need to add Name claim to your ClaimsIdentity, which will automatically be translated to Name property of Identity property.
Here is an example:
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, "SomeName or Id")
};
and add any other claim you need to this list, and then create your ClaimsIdentity:
ClaimsIdentity identity = new ClaimsIdentity(claims, "jwt");
UPDATE:
I did not notice before that you are trying to add Claims (and the whole Identity) inside your authorization process. That is not how it should be. Adding claims should happen inside authentication, not authorization.

How to send bearer token to views in ASP NET MVC 5?

I have a .NET MVC and WEB API project. I want to call the WEB API controllers from javascript but I didn't find a way to send the token to my views. I want to add the bearer token in Viewbag variable, using the below code:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
//GetBearerToken doesn't exists
ViewBag.BearerToken = Request.GetOwinContext().GetBearerToken();
}
In _Layout.cshtml, I added the folowing code to set Authorization header for all ajax requests:
<script type="text/javascript">
window.token = '#Viewbag.BearerToken';
// Add Authorization header on ajax requests
if(window.token) {
(function setAjaxRequestsAuthorizationHeader(token) {
$.ajaxPrefilter(function onAjaxPrefilter(options) {
if (!options.beforeSend) {
options.beforeSend = function onBeforeSend(xhr) {
xhr.setRequestHeader('Authorization', 'Bearer ' + token);
}
}
});
}(window.token));
window.token = null;
}
</script>
Below is my Startup configuration method:
public void ConfigureAuth(IAppBuilder app)
{
// Configure the db context, user manager and signin manager to use a single instance per request
//app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext(AppDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
// Enable the application to use a cookie to store information for the signed in user
// and to use a cookie to temporarily store information about a user logging in with a third party login provider
// Configure the sign in cookie
app.UseCookieAuthentication(new CookieAuthenticationOptions
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/Account/Login"),
Provider = new CookieAuthenticationProvider
{
// Enables the application to validate the security stamp when the user logs in.
// This is a security feature which is used when you change a password or add an external login to your account.
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, User, int>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager),
getUserIdCallback: id => id.GetUserId<int>())
}
});
app.UseExternalSignInCookie(DefaultAuthenticationTypes.ExternalCookie);
PublicClientId = "self";
OAuthOptions = new OAuthAuthorizationServerOptions
{
TokenEndpointPath = new PathString("/Token"),
Provider = new ApplicationOAuthProvider(PublicClientId),
AuthorizeEndpointPath = new PathString("/api/Account/ExternalLogin"),
AccessTokenExpireTimeSpan = TimeSpan.FromDays(14),
// In production mode set AllowInsecureHttp = false
AllowInsecureHttp = true
};
// Enable the application to use bearer tokens to authenticate users
app.UseOAuthBearerTokens(OAuthOptions);
// Enables the application to temporarily store user information when they are verifying the second factor in the two-factor authentication process.
app.UseTwoFactorSignInCookie(DefaultAuthenticationTypes.TwoFactorCookie, TimeSpan.FromMinutes(5));
app.UseTwoFactorRememberBrowserCookie(DefaultAuthenticationTypes.TwoFactorRememberBrowserCookie);
}
To get a token I also tried (in the MVC Controller methods):
var token = new ClaimsPrincipal(User.Identity).Claims.FirstOrDefault(x => x.Type == "access_token")?.Value;
But the value of variable token is always null.
I have also inspected all claims values but I didn't find any possible token.
How can I send the token to controller views?
Solved
I solved the problem by doing the following steps:
In Startup.Auth.cs I added the folowing static property:
public static OAuthAuthorizationServerOptions OAuthOptions { get; private set; }
I have created an extension method for ApplicationUser class:
public static async Task<ClaimsIdentity> GenerateUserIdentityAsync(this ApplicationUser user, UserManager<ApplicationUser, int> manager, string type)
{
// Note the authenticationType must match the one defined in CookieAuthenticationOptions.AuthenticationType
var userIdentity = await manager.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
// Genereate Bearer token
if (manager is ApplicationUserManager && type == DefaultAuthenticationTypes.ApplicationCookie)
{
var accessTokenFormat = Startup.OAuthOptions.AccessTokenFormat;
if (accessTokenFormat != null)
{
IDictionary<string, string> data = new Dictionary<string, string>
{
["userName"] = user.UserName
};
AuthenticationProperties properties = new AuthenticationProperties(data);
AuthenticationTicket ticket = new AuthenticationTicket(userIdentity, properties);
var token = accessTokenFormat.Protect(ticket);
userIdentity.AddClaim(new Claim("Access_Token", token));
}
}
return userIdentity;
}
I modified the method OnActionExecuting to send token to the client:
protected override void OnActionExecuting(ActionExecutingContext filterContext)
{
if (Request.IsAuthenticated)
{
var identity = (ClaimsIdentity)User.Identity;
ViewBag.BearerToken = identity.Claims.FirstOrDefault(x => x.Type == "Access_Token")?.Value;
}
}
In Startup.Auth.cs use the extension method created at step 2 when creating Identity:
OnValidateIdentity = SecurityStampValidator.OnValidateIdentity<ApplicationUserManager, ApplicationUser, int>(
validateInterval: TimeSpan.FromMinutes(30),
regenerateIdentityCallback: (manager, user) => user.GenerateUserIdentityAsync(manager,
DefaultAuthenticationTypes.ApplicationCookie),
getUserIdCallback: id => id.GetUserId<int>())

How to retrieve ClaimsPrincipal from JWT in asp.net core

In my solution, I have two projects. 1) Web API and 2) MVC. I am using ASP.NET Core. API issues JWT token and MVC consumes it to get protected resources. I am using openiddict library to issue JWT. In MVC project, in AccountController Login method, I want to retrieve ClaimsPrincipal (using JwtSecurityTokenHandler ValidateToken method) and assign to HttpContext.User.Claims and HttpContext.User.Identity. I want to store the token in session and for each request after successful login, pass it in header to Web API. I can successfully, issue JWT and consume it in MVC project, but when I try to retrieve ClaimsPrincipal it throws me an error. First of all, I am not even sure whether this retrieving of ClaimsPrinciapal from JWT is a right approach or not. And if it is, what is the way forward.
WebAPI.Startup.CS
public class Startup
{
public static string SecretKey => "MySecretKey";
public static SymmetricSecurityKey SigningKey => new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SecretKey));
public Startup(IHostingEnvironment env)
{
var builder = new ConfigurationBuilder()
.SetBasePath(env.ContentRootPath)
.AddJsonFile("appsettings.json", optional: true, reloadOnChange: true)
.AddJsonFile($"appsettings.{env.EnvironmentName}.json", optional: true)
.AddEnvironmentVariables();
Configuration = builder.Build();
}
public IContainer ApplicationContainer { get; private set; }
public IConfigurationRoot Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public IServiceProvider ConfigureServices(IServiceCollection services)
{
// Add framework services.
services.AddCors();
services.AddMvc().AddJsonOptions(options => { options.SerializerSettings.ContractResolver = new Newtonsoft.Json.Serialization.DefaultContractResolver(); });
services.AddAutoMapper();
services.AddDbContext<MyDbContext>(options =>
{
options.UseMySql(Configuration.GetConnectionString("MyDbContext"));
options.UseOpenIddict();
});
services.AddOpenIddict(options =>
{
options.AddEntityFrameworkCoreStores<TelescopeContext>();
options.AddMvcBinders();
options.EnableTokenEndpoint("/Authorization/Token");
options.AllowPasswordFlow();
options.AllowRefreshTokenFlow();
options.DisableHttpsRequirement();
options.UseJsonWebTokens();
options.AddEphemeralSigningKey();
options.SetAccessTokenLifetime(TimeSpan.FromMinutes(30));
});
var config = new MapperConfiguration(cfg => { cfg.AddProfile(new MappingProfile()); });
services.AddSingleton(sp => config.CreateMapper());
// Create the Autofac container builder.
var builder = new ContainerBuilder();
// Add any Autofac modules or registrations.
builder.RegisterModule(new AutofacModule());
// Populate the services.
builder.Populate(services);
// Build the container.
var container = builder.Build();
// Create and return the service provider.
return container.Resolve<IServiceProvider>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory, IApplicationLifetime applicationLifetime)
{
loggerFactory.AddConsole(Configuration.GetSection("Logging"));
loggerFactory.AddDebug();
app.UseCors(builder => builder.WithOrigins("http://localhost:9001/")
.AllowAnyOrigin());
app.UseJwtBearerAuthentication(new JwtBearerOptions
{
Authority = "http://localhost:9001/",
AutomaticAuthenticate = true,
AutomaticChallenge = true,
Audience = "http://localhost:9000/",
RequireHttpsMetadata = false,
TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidIssuer = "http://localhost:9001/",
ValidateAudience = true,
ValidAudience = "http://localhost:9000",
ValidateLifetime = true,
IssuerSigningKey = SigningKey
}
});
app.UseOpenIddict();
app.UseMvcWithDefaultRoute();
applicationLifetime.ApplicationStopped.Register(() => this.ApplicationContainer.Dispose());
}
}
WebAPI.AuthorizationController.cs which issues JWT.
[Route("[controller]")]
public class AuthorizationController : Controller
{
private IUsersService UserService { get; set; }
public AuthorizationController(IUsersService userService)
{
UserService = userService;
}
[HttpPost("Token"), Produces("application/json")]
public async Task<IActionResult> Exchange(OpenIdConnectRequest request)
{
if (request.IsPasswordGrantType())
{
if (await UserService.AuthenticateUserAsync(new ViewModels.AuthenticateUserVm() { UserName = request.Username, Password = request.Password }) == false)
return Forbid(OpenIdConnectServerDefaults.AuthenticationScheme);
var user = await UserService.FindByNameAsync(request.Username);
var identity = new ClaimsIdentity(OpenIdConnectServerDefaults.AuthenticationScheme, OpenIdConnectConstants.Claims.Name, null);
identity.AddClaim(OpenIdConnectConstants.Claims.Subject, user.UserId.ToString(), OpenIdConnectConstants.Destinations.AccessToken);
identity.AddClaim(OpenIdConnectConstants.Claims.Username, user.UserName, OpenIdConnectConstants.Destinations.AccessToken);
identity.AddClaim(OpenIdConnectConstants.Claims.Email, user.EmailAddress, OpenIdConnectConstants.Destinations.IdentityToken);
identity.AddClaim(OpenIdConnectConstants.Claims.GivenName, user.FirstName, OpenIdConnectConstants.Destinations.IdentityToken);
identity.AddClaim(OpenIdConnectConstants.Claims.MiddleName, user.MiddleName, OpenIdConnectConstants.Destinations.IdentityToken);
identity.AddClaim(OpenIdConnectConstants.Claims.FamilyName, user.LastName, OpenIdConnectConstants.Destinations.IdentityToken);
identity.AddClaim(OpenIdConnectConstants.Claims.EmailVerified, user.IsEmailConfirmed.ToString(), OpenIdConnectConstants.Destinations.IdentityToken);
identity.AddClaim(OpenIdConnectConstants.Claims.Audience, "http://localhost:9000", OpenIdConnectConstants.Destinations.AccessToken);
var principal = new ClaimsPrincipal(identity);
return SignIn(principal, OpenIdConnectServerDefaults.AuthenticationScheme);
}
throw new InvalidOperationException("The specified grant type is not supported.");
}
}
MVC.AccountController.cs contains Login, GetTokenAsync method.
public class AccountController : Controller
{
public static string SecretKey => "MySecretKey";
public static SymmetricSecurityKey SigningKey => new SymmetricSecurityKey(Encoding.ASCII.GetBytes(SecretKey));
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Login(LoginVm vm, string returnUrl = null)
{
ViewData["ReturnUrl"] = returnUrl;
if (ModelState.IsValid)
{
var token = await GetTokenAsync(vm);
SecurityToken validatedToken = null;
TokenValidationParameters validationParameters = new TokenValidationParameters()
{
ValidateIssuer = true,
ValidIssuer = "http://localhost:9001/",
ValidateAudience = true,
ValidAudience = "http://localhost:9000",
ValidateLifetime = true,
IssuerSigningKey = SigningKey
};
JwtSecurityTokenHandler handler = new JwtSecurityTokenHandler();
try
{
ClaimsPrincipal principal = handler.ValidateToken(token.AccessToken, validationParameters, out validatedToken);
}
catch (Exception e)
{
throw;
}
}
return View(vm);
}
private async Task<TokenVm> GetTokenAsync(LoginVm vm)
{
using (var client = new HttpClient())
{
var request = new HttpRequestMessage(HttpMethod.Post, $"http://localhost:9001/Authorization/Token");
request.Content = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "password",
["username"] = vm.EmailAddress,
["password"] = vm.Password
});
var response = await client.SendAsync(request, HttpCompletionOption.ResponseContentRead);
response.EnsureSuccessStatusCode();
var payload = await response.Content.ReadAsStringAsync();
//if (payload["error"] != null)
// throw new Exception("An error occurred while retriving an access tocken.");
return JsonConvert.DeserializeObject<TokenVm>(payload);
}
}
}
Error I am getting: "IDX10501: Signature validation failed. Unable to match 'kid': '0-AY7TPAUE2-ZVLUVQMMUJFJ54IMIB70E-XUSYIB', \ntoken: '{\"alg\":\"RS256\",\"typ\":\"JWT\",\"kid\":\"0-AY7TPAUE2-ZVLUVQMMUJFJ54IMIB70E-XUSYIB\"}.{\"sub\":\"10\",\"username\":\"...
See this thread because I was looking for the very same thing (not the exception though), and the accepted answer indeed helps the OP, however, it doesn't help me with : how to create ClaimsPrincipal from JWT Token.
After some research and digging, I've found a way to do it manually (it was my case, I had to do it manually in a specific case).
To do so, first, parse the token with JwtSecurityTokenHandler class :
var token = new JwtSecurityTokenHandler().ReadJwtToken(n.TokenEndpointResponse.AccessToken);
After that, you just ned to create a new ClaimsPrincipal :
var identity = new ClaimsPrincipal(new ClaimsIdentity(token.Claims));
In my specific case, I just have to update claims on my already authenticated user, so I use this code :
var identity = (ClaimsIdentity)User.Identity;
identity.AddClaims(token.Claims);
Hope it will help someone one day if looking after the answer for the title.
First of all, I am not even sure whether this retrieving of ClaimsPrinciapal from JWT is a right approach or not.
It's likely not the approach I'd personally use. Instead, I'd simply rely on the JWT middleware to extract the ClaimsPrincipal from the access token for me (no need to manually use JwtSecurityTokenHandler for that).
The exception thrown by IdentityModel is actually caused by a very simple root cause: you've configured OpenIddict to use an ephemeral RSA asymmetric signing key (via AddEphemeralSigningKey()) and registered a symmetric signing key in the JWT bearer options, a scenario that can't obviously work.
You have two options to fix that:
Register your symmetric signing key in the OpenIddict options using AddSigningKey(SigningKey) so OpenIddict can use it.
Use an asymmetric signing key (by calling AddEphemeralSigningKey(), or AddSigningCertificate()/AddSigningKey() on production) and let the JWT bearer middleware use it instead of your symmetric signing key. For that, remove the entire TokenValidationParameters configuration to allow IdentityModel to download the public signing key from OpenIddict's discovery endpoint.

Categories

Resources