I have added Auth to my swagger UI and at the same time I have also added token through Postman. But every time I try to add or post something to my services. Then it comes up and tells 401 error.
But on the other hand, I remove the Auth part. Then I have no problems posting or adding anything to the database.
I have try this here.
builder.Services.AddSwaggerGen(opt =>
{
opt.SwaggerDoc("v1", new OpenApiInfo { Title = "MyAPI", Version = "v1" });
opt.AddSecurityDefinition("Bearer", //Name the security scheme
new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme.",
Type = SecuritySchemeType.Http, //We set the scheme type to http since we're using bearer authentication
Scheme = JwtBearerDefaults.AuthenticationScheme //The name of the HTTP Authorization scheme to be used in the Authorization header. In this case "bearer".
});
opt.AddSecurityRequirement(new OpenApiSecurityRequirement{
{
new OpenApiSecurityScheme{
Reference = new OpenApiReference{
Id = JwtBearerDefaults.AuthenticationScheme, //The name of the previously defined security scheme.
Type = ReferenceType.SecurityScheme
}
},new List<string>()
}
});
});
builder.Services
.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(r =>
{
r.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = false,
ValidateAudience = false,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(token.ToString())),
ClockSkew = TimeSpan.Zero
};
});
builder.Services.AddAuthorization(r =>
{
r.AddPolicy("adminAccessData", p => p.RequireClaim("role", "Admin"));
});
When I try to post or look like a controller, I have this piece up at the top of the controller.
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Policy = "Admin")]
What I am trying to do. it is that I only want to post or leave something in the database when I am Admin. I have been assigned a role as "Admin" on my Claims when I log in to the site.
Related
From what I have researched, then it's best practice to seperate the API and IdentityServer into 2 projects (also 2 domains-i.e.: https://api.mywebsite.com and https://identity.mywebsite.com), so I did that.
In my IdentityServer/AuthenticationController.cs # login-method it is currently making a JWT-token with a list of Claims(role(s), name, Jti(idk what that is), email, etc, that it puts into the JwtSecurityToken.
How does i.e. [Authorize(Roles = "Admin")] in my WebAPI/ProductController.cs communicate with my IdentityServer and then authorizing it. Is it in "AddAuthentication(...);" in Program.cs services? Or in AddJwtBearer(...)? Or specifically in ValidIssuer?
Where is the connection between API(i.e. https://api.mywebsite.com) and IdentityServer(https://identityserver.mywebsite.com)?
My IdentityServer-project Program.cs:
...
builder.Services.AddIdentityServer().AddAspNetIdentity<IdentityUser>().AddClientStore<InMemoryClientStore>().AddResourceStore<InMemoryResourcesStore>();
builder.Services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
ValidAudiences = builder.Configuration.GetSection("JWT:ValidAudiences").Get<string[]>(),
ValidIssuer = builder.Configuration["JWT:ValidIssuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JWT:Secret"]))
};
});
...
My API-project Program.cs:
...
builder.Services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters()
{
ValidateIssuerSigningKey = true,
ValidAudiences = builder.Configuration.GetSection("JWT:ValidAudiences").Get<string[]>(),
ValidIssuer = builder.Configuration["JWT:ValidIssuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(builder.Configuration["JWT:Secret"]))
};
});
...
Also, are my both codeblocks well-written when it comes to IdentityServer and API authentication and authorization?
AddJwtBearer in the API is in charge of taking the incoming access token and converting it to a "ClaimsPrincipal" user. Then that user is passed with all its claim to the authorization handler that will determine if the user is allowed to pass or not.
For example the [Authorize(Roles = "Admin")] attribute will check the role claims in the user if it contains the Admin role claim or not.
Usually you don't need to specify the IssuerSigningKey in AddJwtBearer and instead let AddJwtBearer to automatically retrieve it from IdentityServer directly at startup. (just remove IssuerSigningKey). You typically add IssuerSigningKey if the API can't talk to IdentityServer.
I have inherited an ASP.NET Core 5 MVC application that has integrated the authentication of the users with a third-party identity server, WSO2.
The authentication is working fine, but I'm not able to understand how to retrieve the username of the user that has logged in.
Here is the code:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddSession();
services.AddControllersWithViews();
services.AddTransient<IUserRepository, UserRepository>();
services.AddTransient<ITokenService, TokenService>();
services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
//auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["Jwt:Issuer"],
ValidAudience = Configuration["Jwt:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"]))
};
});
services.AddHttpClient();
}
in the controller I have added:
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme, Roles = "Administrator,Supervisor")]
Is the name and roles found in the JWT token?
If, so then the issue is that OpenIDConnect and Microsoft do not agree about what the name of the 'name' claim should be.
To address this, you need to tell AddJwtBearer, which claim is the 'name' and 'role' claim using:
.AddMyJwtBearer(opt =>
{
...
opt.TokenValidationParameters.RoleClaimType = "roles";
opt.TokenValidationParameters.NameClaimType = "name";
I'm trying to set up an ASP.Net 5 web project with JWTBearer authentication for API Controllers (Controllers decorated with the APIController attribute) and CookieAuthentication for MVC Controllers (Controller classes derived from Controller, to render Views). The relevant part of my ConfigureServices method in the Startup Class looks like this:
services
.AddAuthentication(options =>
{
options.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(options =>
{
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes("My Secret Key")),
ValidateAudience = false,
ValidateIssuer = false,
RequireExpirationTime = false,
ValidateLifetime = true
};
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.LoginPath = "/account/login";
options.LogoutPath = "/account/logout";
options.AccessDeniedPath = "/account/accessdenied";
options.Cookie = new CookieBuilder {SameSite = SameSiteMode.Strict};
options.ExpireTimeSpan = TimeSpan.FromMinutes(20);
});
var schemePolicy = new AuthorizationPolicyBuilder(
CookieAuthenticationDefaults.AuthenticationScheme,
JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
services.AddAuthorization(o => o.DefaultPolicy = schemePolicy);
In an MCV Controller I have an Action like the below:
[Authorize(AuthenticationSchemes = CookieAuthenticationDefaults.AuthenticationScheme)]
public IActionResult AnActionForAuthenticatedUsers()
{
// do some stuff...
return View();
}
Now I would expect that when an unauthenticated request for this action comes in the request will be forwarded to /account/login but it is not. Instead a 401 statuscode is returned.
The webserver says:
info: Microsoft.AspNetCore.Authorization.DefaultAuthorizationService[2]
Authorization failed. These requirements were not met:
DenyAnonymousAuthorizationRequirement: Requires an authenticated user.
info: Microsoft.AspNetCore.Authentication.Cookies.CookieAuthenticationHandler[12]
AuthenticationScheme: Cookies was challenged.
info: Microsoft.AspNetCore.Authentication.JwtBearer.JwtBearerHandler[12]
AuthenticationScheme: Bearer was challenged.
What am I missing here?
The problem here is not in Authentication policy, but in Authorization configuration
Create a separate policy for each type of Authorization and that would work as you are expecting:
services.AddAuthorization(o =>
{
o.AddPolicy(CookieAuthenticationDefaults.AuthenticationScheme, schemePolicy);
o.AddPolicy(JwtBearerDefaults.AuthenticationScheme, schemePolicy);
});
I have this config in IdentityServer:
public static IEnumerable<ApiResource> ApiResources =>
new ApiResource[]
{
new ApiResource
{
Name = "MyApi"
}
};
and this jwt configuration on the ASP.NET Core web API:
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
//identity server
options.Authority = "https://localhost:5001";
//access token recepient
options.Audience = "https://localhost:5001/resources";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = true,
ValidateLifetime = true,
};
});
I'm expecting that the Web API authentication won't accept the token from IdentityServer because the Web API JwtBearerOption.Audience is not equal to "MyApi". But in my config the Audience is only validated if the audience is set to "https://localhost:5001/resources" and will invalidate if I set it to "MyApi"
IdentityServer4 documentation about related to my question.
To get MyApi into the list of audiences you need to define an ApiScope as will in IdentityServer4 (v4.0x)
See this articles for more details
I'm currently using Ocelot as Api Gateway for a micro-services architecture. I have some authenticated reroutes and to be able to use it I declared a authentication Middleware like this :
var authenticationProviderKey = "Authentification";
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(authenticationProviderKey, x =>
{
x.RequireHttpsMetadata = false;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(Encoding.ASCII.GetBytes(token.Secret)),
ValidIssuer = token.Issuer,
ValidAudience = token.Audience,
ValidateIssuer = true,
ValidateAudience = true
};
});
I wanted to run some custom validation to implement refresh token workflow, to do so I implemented the preAuthentication Middleware to make so tests :
PreAuthenticationMiddleware = async (ctx, next) =>
{
IEnumerable<string> header;
ctx.DownstreamRequest.Headers.TryGetValues("Authorization", out header);
if (header.FirstOrDefault() != null)
{
if (JwtUtils.ValidateExpirationToken(header.FirstOrDefault()))
{
//On validate refresh token
Console.WriteLine("PreAuthentification Middleware");
Tuple<int, string> credentials = JwtUtils.retrieveInfos(header.FirstOrDefault());
string token = JwtUtils.GenerateToken(credentials.Item1, credentials.Item2);
ctx.DownstreamRequest.Headers.Remove("Authorization");
ctx.DownstreamRequest.Headers.Add("Authorization", token);
await next.Invoke();
}
}
}
From what I understood, when I make an api Call, the preAuthenticate Middleware would be called, and with next.Invoke() my Authentication middleware would be called. The newly generated token in my PreAuthentication middleware is a valid one, but my authentication middleware throws an expiredToken exception even tho he's not. Therefore I think the authentication Middleware is run against the first JWT when the new one has not been yet set to the Authorization Header. Is it the attended behaviour? Or maybe I did not understood correctly the middleware in Ocelot?
Anyway, some help would be much appreciated !
Have a good day,
Lio