.Net Core 3.1 - Angular10 Authentication and Antiforgery - authentication

I implemented a simple app in .Net Core 3.1. I am using JWT token as Authentication and added Antiforgery like this in ConfigureServices()
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Environment.GetEnvironmentVariable("JWT_SEED"))),
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
options.Events = new JwtBearerEvents
{
OnMessageReceived = context =>
{
context.Token = context.Request.Cookies["JWT"];
return Task.CompletedTask;
}
};
});
services.AddAuthorization(options =>
options.AddPolicy("ValidAccessToken", policy =>
{
policy.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
policy.RequireAuthenticatedUser();
})
);
services.AddAntiforgery(options => {
options.HeaderName = "X-XSRF-TOKEN";
options.SuppressXFrameOptionsHeader = false;
});
Then, in Configure() I have:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IAntiforgery antiforgery)
{
app.UseMiddleware<ExceptionMiddleware>();
app.UseCors(options => options
.WithOrigins("https://abc.random.com")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.Use(next => context => {
string path = context.Request.Path.Value;
if (path != null && path.ToLower().Contains("/api")) {
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN", tokens.RequestToken,
new CookieOptions() {
HttpOnly = false,
Secure = true
});
}
return next(context);
});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
I also have a method to refresh the antiforgery token
[HttpGet("refreshAntiforgery")]
public IActionResult RefreshAntiforgery() {
var antiforgeryTokens = _antiforgery.GetAndStoreTokens(HttpContext);
HttpContext.Response.Cookies.Append(
"XSRF-TOKEN",
antiforgeryTokens.RequestToken,
new CookieOptions
{
HttpOnly = false,
Secure = true
}
);
return NoContent();
}
In my Angular app I registered in app.module.ts
HttpClientXsrfModule.withOptions({
cookieName: 'XSRF-TOKEN',
headerName: 'X-XSRF-TOKEN'
}),
and in the services I want to use the antiforgery token (I also have the interceptor to add credentials for all requests and also have URLs starting with // to avoid relative URLs)
updatePassword(client: Client): Observable<any> {
let xsrfToken = this.xsrfTokenExtractor.getToken() as string;
let headers = new HttpHeaders()
.set('X-XSRF-TOKEN', xsrfToken);
return this.http.put<Client>(
environment.baseUrls.client +
environment.relativeUrls.updatePassword,
user,
{
headers: headers,
withCredentials: true
}
);
}
The problem is that XSRF-TOKEN coming from .net core app is being set as HttpOnly despite I set the cookie as HttpOnly = false
cookie from browser
Any idea why HttpOnly is being set in XSRF-TOKEN? (I have tried different solutions like cookie policies or check Configure() order, clear cookies from browser, but nothing changes the value)

Related

403 on Succeed AuthorizationHandler aspnetcore

Trying to implement policy authorization in a .NET6 Web Api.
I've added in authentication and authorization via the configuration:
builder.AddCredibleApplicationFramework(assemblies);
builder.Services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme,
options =>
{
var tokenValidationSettings = builder.Configuration.GetSection("JwtSettings")
.Get<TokenValidationSettings>();
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = tokenValidationSettings.ValidateIssuer,
ValidateAudience = tokenValidationSettings.ValidateAudience,
ValidateIssuerSigningKey = true,
ValidIssuer = tokenValidationSettings.Issuer,
IssuerSigningKey =
new SymmetricSecurityKey(Encoding.ASCII.GetBytes(tokenValidationSettings.SigningKey))
};
});
builder.Services.AddAuthorization(options =>
{
options.AddPolicy("isAdministrator",
policyBuilder =>
{
policyBuilder.AuthenticationSchemes.Add(JwtBearerDefaults.AuthenticationScheme);
policyBuilder.AddRequirements(new IsAdministratorRequirement());
});
});
builder.Services.AddSingleton<IAuthorizationHandler, IsApplicationAdministratorHandler>();
builder.Services.AddDbContext<CDbContext>(options =>
{
var connection = builder.Configuration.GetConnectionString("conn");
options
.UseMySql(connection, ServerVersion.AutoDetect(connection))
.EnableSensitiveDataLogging()
.EnableDetailedErrors()
.UseLoggerFactory(loggerFactory);
});
var app = builder.Build();
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
}
app.UseAuthentication();
app.UseAuthorization();
app.UseHttpsRedirection();
app.MapControllers();
app.Run();
The handler success path is executing (the requirement succeeds):
public class IsApplicationAdministratorHandler : AuthorizationHandler<IsAdministratorRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
IsAdministratorRequirement requirement)
{
if (context.User.HasClaim(c => c.Type.Equals("applicationAdmin")) ||
context.User.HasClaim(c => c.Type.Equals("platformAdmin")))
{
return Task.FromResult(() => context.Succeed(requirement)); <-- This line returns....
}
return Task.FromResult(() => context.Fail());
}
}
I am getting 403 even though the handler succeeds.
You are returning a lambda result rather than calling the actual context method, since your lambda is never executed. Its not necessary in your case, as you can directly call context.Succeed(requirement)
public class IsApplicationAdministratorHandler : AuthorizationHandler<IsAdministratorRequirement>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context,
IsAdministratorRequirement requirement)
{
if (context.User.HasClaim(c => c.Type.Equals("applicationAdmin")) ||
context.User.HasClaim(c => c.Type.Equals("platformAdmin")))
{
context.Succeed(requirement);
}
else
{
// this isn't strictly necessary, unless you want to fail hard and early
// and not give other policy handlers to be evaluated
context.Fail(requirement)
};
return Task.Completed;
}
}

The authorization is not working in ASP.net core web API

I'm trying to implement authorization in asp.ne core webapi web application using jwt tokens.
but when I send a request with bearer authorization and the jwt token generated, the response is always 401 unauthorized
I'm using .Net 5.0 version
what I'm doing wrong ?
here is my startup.cs file
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.Configure<JwtSettings>(Configuration.GetSection("Jwt"));
var jwtSettings = Configuration.GetSection("Jwt").Get<JwtSettings>();
services.AddControllers();
var dataAssemblyName = typeof(CRMContext).Assembly.GetName().Name;
services.AddDbContext<CRMContext>(options => options.UseSqlServer(Configuration.GetConnectionString("Default"), x => x.MigrationsAssembly(dataAssemblyName)));
services.AddIdentity<User, Role>(options =>
{
options.Password.RequiredLength = 8;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = true;
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(1d);
options.Lockout.MaxFailedAccessAttempts = 5;
})
.AddEntityFrameworkStores<CRMContext>()
.AddDefaultTokenProviders();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["JwtAuthentication:JwtIssuer"],
ValidAudience = Configuration["JwtAuthentication:JwtIssuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtAuthentication:JwtKey"])),
};
});
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddTransient<IAccountService, AccountService>();
services.AddTransient<IApplicationUserService, ApplicationUserService>();
services.AddMvc().AddControllersAsServices();
services.AddCors(o => o.AddPolicy("MyPolicy", builder =>
{
builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader();
}));
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo { Title = "Cloud 9", Version = "v1" });
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT containing userid claim",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
});
var security =
new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Id = "Bearer",
Type = ReferenceType.SecurityScheme
},
UnresolvedReference = true
},
new List<string>()
}
};
options.AddSecurityRequirement(security);
});
services.AddAutoMapper(typeof(Startup));
services.AddControllersWithViews()
.AddNewtonsoftJson(options =>
options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new MappingProfile());
});
IMapper mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
services.AddAuth(jwtSettings);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
MyServiceProvider.ServiceProvider = app.ApplicationServices;
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
// app.UseSwagger();
// app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "Ids.Crm.Api v1"));
app.UseAuthorization();
app.UseAuth();
app.UseCors("MyPolicy");
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseSwagger()
.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "test v1");
c.ConfigObject.AdditionalItems.Add("syntaxHighlight", false);
c.ConfigObject.AdditionalItems.Add("theme", "agate");
});
}
Your request pipeline is missing the authentication middleware. So you couldn't possibly authenticate or possibly authorize. Simply add the middleware before the authorization middleware in the Configure method
app.UseAuthentication();
app.UseAuthorization();
Update: It seems app.UseAuth(); is your authentication middleware. If it is, then place it above the Authorization middleware.
app.UseAuth();
app.UseAuthorization();
I found the solution.... I was declaring a key in the generation of the token which is different from the one I've declared in the appsetting.json
so when the TokenValidationParameters takes the wrong key it was preventing authorization

Cannot access [Authorize] controllers with cookie authentication

I'm using .NET Core 3.1 and Cookie authentication to login.
I saw the cookie generated as the picture below, but I still cannot access [Authorize] controllers.
This is my code in Startup.cs
ConfigureServices(IServiceCollection services)
services.Configure<CookiePolicyOptions>(options =>
{
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.Lax;
});
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.Cookie.Name = "SRLoginCookie";
options.Cookie.HttpOnly = true;
options.LoginPath = new PathString("/users/login");
options.ExpireTimeSpan = TimeSpan.FromDays(1);
options.SlidingExpiration = false;
});
services.PostConfigure<CookieAuthenticationOptions>(IdentityConstants.ApplicationScheme,
options =>
{
options.LoginPath = "/users/login";
});
Configure(IApplicationBuilder app)
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
Login code
[HttpPost]
[Route("login")]
[AllowAnonymous]
public async Task<ActionResult> Login([FromForm]LoginRequest model)
{
var identity = new ClaimsIdentity(CookieAuthenticationDefaults.AuthenticationScheme);
identity.AddClaim(new Claim(ClaimTypes.Email, model.Email));
ClaimsPrincipal principal = new ClaimsPrincipal(identity);
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
principal);
//if (HttpContext.User.Identity.IsAuthenticated)
return RedirectToAction("Index", "Home");
}
The cookie was generated, but can't access home/index as you see. What is my problem? Many thanks for help!
I found the solution after posting this question for a while.
Just need [Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)] instead of [Authorize], everthing solved.
Try this:
builder.Services
.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(JwtBearerDefaults.AuthenticationScheme, options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = builder.Configuration["Jwt:Issuer"],
ValidAudience = builder.Configuration["Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["Jwt:Key"]))
};
options.Events = new JwtBearerEvents()
{
OnMessageReceived = context =>
{
// get token from cookie
if (context.Request.Cookies.ContainsKey("SRLoginCookie"))
{
context.Token = context.Request.Cookies["SRLoginCookie"];
}
return Task.CompletedTask;
}
};
});

With JWT token and policy set, I get unauthorized 401

I follow the tutorial link below.
https://fullstackmark.com/post/13/jwt-authentication-with-aspnet-core-2-web-api-angular-5-net-core-identity-and-facebook-login
I am trying to understand how it works and I want to use role-based authentication using this token. so I made another policy in the Startup.cs file as below.
And I tried to use it like [Authorize(Policy = "admin")] the controller but every time I try I get unauthenticated using postman.
What am I missing? how to make roles-based authentication based on the tutorial?
Configure
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.WithOrigins("http://localhost:4200", "http://localhost:44318")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
var jwtAppSettingOptions = Configuration.GetSection(nameof(JwtIssuerOptions));
// Configure JwtIssuerOptions
services.Configure<JwtIssuerOptions>(options =>
{
options.Issuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
options.Audience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)];
options.SigningCredentials = new SigningCredentials(_signingKey, SecurityAlgorithms.HmacSha256);
});
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)],
ValidateAudience = true,
ValidAudience = jwtAppSettingOptions[nameof(JwtIssuerOptions.Audience)],
ValidateIssuerSigningKey = true,
IssuerSigningKey = _signingKey,
RequireExpirationTime = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(configureOptions =>
{
configureOptions.ClaimsIssuer = jwtAppSettingOptions[nameof(JwtIssuerOptions.Issuer)];
configureOptions.TokenValidationParameters = tokenValidationParameters;
configureOptions.SaveToken = true;
});
// api user claim policy
services.AddAuthorization(options =>
{
options.AddPolicy("ApiUser", policy => policy.RequireClaim(Constants.Strings.JwtClaimIdentifiers.Rol, Constants.Strings.JwtClaims.ApiAccess));
});
services.AddAuthorization(options =>
options.AddPolicy("admin", policy => policy.RequireRole("admin"))
);
var builder = services.AddIdentityCore<AppUser>(o =>
{
// configure identity options
o.Password.RequireDigit = false;
o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false;
o.Password.RequireNonAlphanumeric = false;
o.Password.RequiredLength = 6;
});
builder = new IdentityBuilder(builder.UserType, typeof(IdentityRole), builder.Services);
builder.AddEntityFrameworkStores<CDSPORTALContext>().AddDefaultTokenProviders().AddRoles<IdentityRole>();
//.AddRoles<IdentityRole>()
services.AddControllers();
services.AddAutoMapper(typeof(Startup));
services.AddSingleton<IJwtFactory, JwtFactory>();
services.TryAddTransient<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<IRegionRepository, RegionRepository>();
services.AddScoped<IRegionService, RegionService>();
services.AddScoped<IEmailHelper, EmailHelper>();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseCors("CorsPolicy");
app.UseExceptionHandler(
builder =>
{
builder.Run(
async context =>
{
context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
context.Response.Headers.Add("Access-Control-Allow-Origin", "*");
var error = context.Features.Get<IExceptionHandlerFeature>();
if (error != null)
{
context.Response.AddApplicationError(error.Error.Message);
await context.Response.WriteAsync(error.Error.Message).ConfigureAwait(false);
}
});
});
app.UseRouting();
app.UseStaticFiles();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseSpa(spa =>
{
spa.Options.SourcePath = "Client";
spa.UseAngularCliServer(npmScript: "start");
});
}
}
Auth Controller
// POST api/auth/login
[HttpPost("login")]
public async Task<IActionResult> Post([FromBody]CredentialsViewModel credentials)
{
if (!ModelState.IsValid)
{
return BadRequest(ModelState);
}
var identity = await GetClaimsIdentity(credentials.UserName, credentials.Password);
if (identity == null)
{
//return null;
return BadRequest(Error.AddErrorToModelState("login_failure", "Invalid username or password.", ModelState));
}
var id = identity.Claims.Single(c => c.Type == "id").Value;
var user = await _userManager.FindByIdAsync(id);
IList<string> role = await _userManager.GetRolesAsync(user);
var jwt = await Tokens.GenerateJwt(identity, role[0], _jwtFactory, credentials.UserName, _jwtOptions, new JsonSerializerSettings { Formatting = Formatting.Indented });
return new OkObjectResult(jwt);
}
I tried with all method and none of them working
[Authorize(Policy = "ApiUser")]
[HttpGet("getPolicy")]
public string GetPolicy()
{
return "policyWorking";
}
[Authorize(Roles = "admin")]
[HttpGet("getAdmin")]
public string GetAdmin()
{
return "adminWorking";
}
[Authorize ]
[HttpGet("getAuthorize")]
public string GetAuthorize()
{
return "normal authorize Working";
}
Decoded Token
"jti": "840d507d-b2d0-454b-bd1f-007890d3e669",
"iat": 1587699300,
"rol": "api_access",
"id": "1ac370e2-f5e9-4404-b017-8a3c087e2196",
"nbf": 1587699299,
"exp": 1587706499
I forgot to add this to appsetting.
"JwtIssuerOptions": {
"Issuer": "webApi",
"Audience": "http://localhost:4200/"
}

How to validate Cognito JWT with empty audience correctly and authenticate after?

I use aps.net core with JWT authentication and I found that aws cognito returns wrong token. Instead aud it returns client_id in access token.
I use Nuget libraries
AWSSDK.Core
AWSSDK.CognitoIdentityProvider
Amazon.Extensions.CognitoAuthentication
It was the same result. For examaple:
Access token is:
{
"sub": "9ed87b45-da04-4fda-bc74-XXXXXXXXXXXX",
"event_id": "469880d0-8b17-417a-88d7-XXXXXXXXXXXX",
"token_use": "access",
"scope": "aws.cognito.signin.user.admin",
"auth_time": 1583252488,
"iss": "https://cognito-idp.us-east-2.amazonaws.com/us-east-2_XXXXXXXX",
"exp": 1583256088,
"iat": 1583252488,
"jti": "c1ca9561-51ce-4b57-9f51-3355363fb4f6",
"client_id": "AppClientIDXXXXXXXXXXXXX",
"username": "testname"
}
After all I found that id token returns with 'aud'
{
"sub": "9ed87b45-da04-4fda-bc74-XXXXXXXXXXXX",
"aud": "AppClientIDXXXXXXXXXXXXX",
"email_verified": true,
"event_id": "469880d0-8b17-417a-88d7-XXXXXXXXXXXX",
"token_use": "id",
"auth_time": 1583252488,
"iss": "https://cognito-idp.us-east-2.amazonaws.com/us-east-2_XXXXXXXX",
"cognito:username": "testname",
"exp": 1583256088,
"iat": 1583252488,
"email": "testname#mail.no"
}
I used two way of adding jwt authentication. It didn't work for me.
Example 1:
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKeyResolver = (s, securityToken, identifier, parameters) =>
{
var json = new WebClient().DownloadString(parameters.ValidIssuer + "/.well-known/jwks.json");
return JsonConvert.DeserializeObject<JsonWebKeySet>(json).Keys;
},
ValidateIssuer = true,
ValidIssuer = $"https://cognito-idp.{region}.amazonaws.com/{poolId}",
ValidateAudience = true,
ValidAudience = appClientId,
};
});
Example 2:
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.Audience = appClientId;
options.Authority = $"https://cognito-idp.{region}.amazonaws.com/{poolId}";
});
My authentication code
using AWSSDK.CognitoIdentityProvider
var initiateAuthRequest = new InitiateAuthRequest()
{
ClientId = myClientId,
AuthFlow = AuthFlowType.USER_PASSWORD_AUTH,
};
initiateAuthRequest.AuthParameters.Add("USERNAME", user.Username);
initiateAuthRequest.AuthParameters.Add("PASSWORD", user.Password);
var authResponse = await _cognitoIdentityProvider.InitiateAuthAsync(initiateAuthRequest);
using Amazon.Extensions.CognitoAuthentication
var provider = new AmazonCognitoIdentityProviderClient(new EnvironmentVariablesAWSCredentials(), myRegion);
var userPool = new CognitoUserPool(myPool, myClient, provider);
var usr = new CognitoUser(user.Username, myClient, userPool, provider);
AuthFlowResponse authResponse = await usr.StartWithSrpAuthAsync(
new InitiateSrpAuthRequest(){Password = user.Password}).ConfigureAwait(false);
How to get valid JWT token? How to validate token correctly?
Updated:
In Example 1 if I set ValidateAudience to false and removed ValidAudience I get 401 error
ValidateAudience = false,
//ValidAudience = appClientId
My Startup.cs is
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public static IConfiguration Configuration { get; private set; }
public void ConfigureServices(IServiceCollection services)
{
var region = Configuration[Resources.AWSRegion];
var poolId = Configuration[Resources.AWSPoolId];
var appClientId = Configuration[Resources.AWSClientId];
services.AddSingleton<IAmazonCognitoIdentityProvider>(provider => new AmazonCognitoIdentityProviderClient(RegionEndpoint.USEast2)));
services.AddControllers();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKeyResolver = (s, securityToken, identifier, parameters) =>
{
var json = new WebClient().DownloadString(parameters.ValidIssuer + "/.well-known/jwks.json");
return JsonConvert.DeserializeObject<JsonWebKeySet>(json).Keys;
},
ValidateIssuer = true,
ValidIssuer = $"https://cognito-idp.{region}.amazonaws.com/{poolId}",
ValidateAudience = true,
ValidAudience = appClientId
};
});
services.AddAuthorization();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.UseAuthentication();
}
}
This worked out for me, I used id token instead of access token
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = GetCognitoTokenValidationParams();
});
}
private TokenValidationParameters GetCognitoTokenValidationParams()
{
var cognitoIssuer = $"https://cognito-idp.{Configuration["AWS:Region"]}.amazonaws.com/{Configuration["AWS:UserPoolId"]}";
var jwtKeySetUrl = $"{cognitoIssuer}/.well-known/jwks.json";
var cognitoAudience = Configuration["AWS:UserPoolClientId"];
return new TokenValidationParameters
{
IssuerSigningKeyResolver = (s, securityToken, identifier, parameters) =>
{
// get JsonWebKeySet from AWS
var json = new WebClient().DownloadString(jwtKeySetUrl);
return JsonConvert.DeserializeObject<JsonWebKeySet>(json).Keys;
},
ValidIssuer = cognitoIssuer,
ValidateIssuerSigningKey = true,
ValidateIssuer = true,
ValidateLifetime = true,
ValidAudience = cognitoAudience
};
}
I think it is because you use the access jwt token returned from Cognito during sign-in. Use id token instead.
return new Promise((resolve, reject) => {
return user.authenticateUser(authenticationDetails, {
onFailure: (err) => {
reject(err);
},
onSuccess: (result) => {
resolve(new SignInResponse(result.getIdToken().getJwtToken()));
},
});
});
I read several answers and the majority recomended setting
ValidateAudience = false
Since I didn't want to do that, because it will avoid validating the correct audience, I ended up defining AudienceValidator, to do it you have to define a delegate in the options when configuring your application. So in Startup you have to include this:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Audience = configuration["AWS:Cognito:ClientId"];
options.Authority = configuration["AWS:Cognito:Authority"];
options.TokenValidationParameters.AudienceValidator = (audiences, securityToken, validationParameters) =>
{
//This is necessary because Cognito tokens doesn't have "aud" claim. Instead the audience is set in "client_id"
return validationParameters.ValidAudience.Contains(((JwtSecurityToken)securityToken).Payload["client_id"].ToString());
};
});
This will check that the claim "client_id" corresponds with the audience you're expecting
I had the same issue and I have found only one solution.
If you are going to use an access token to authenticate user, set
ValidateAudience = false,
//ValidAudience = appClientId
and add AuthenticationSchemes to Authorize filter like that:
[HttpGet]
[Authorize(AuthenticationSchemes = "Bearer")]
public async Task<string> Test()
{
return await Task.Run(() => "hello world");
}
I have very similar code and it worked for me. Hope it helps for you as well.
Also you can replace this code:
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
To that (just to simplify):
services.AddAuthentication("Bearer")