Identityserver implicit flow unauthorized_client - c#

I cannot seem to understand why do I get unauthorized_client from identityserver. I use oidc-client with Angular 4 ui and asp.net core of web APIs. I cannot connect to identity server as every time it is returning my client is unauthorized_client.
This is the registered client:
new Client
{
ClientId = "EStudent",
ClientName = "EStudent",
AllowedGrantTypes = GrantTypes.Implicit,
RequireClientSecret = false,
AllowAccessTokensViaBrowser = true,
AllowedCorsOrigins = { "http://localhost:63150" },
LogoutSessionRequired = false,
RequireConsent = false,
AllowedScopes = new List<string>
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"UsersAPI",
},
AlwaysIncludeUserClaimsInIdToken = true,
RedirectUris = {
"http://localhost:63150/oauth.html"
},
PostLogoutRedirectUris = {
"http://localhost:63150/",
$"{this._baseAddress}/index.html"
},
AllowOfflineAccess = true,
}
This is the auth service in Angular:
import { Injectable, EventEmitter } from '#angular/core';
import { Http, Headers, RequestOptions, Response } from '#angular/http';
import { Observable } from 'rxjs/Rx';
import { UserManager, User } from 'oidc-client';
import { environment } from '../../../environments/environment';
const settings: any = {
authority: 'http://localhost:8200/oauth',
client_id: 'EStudent',
redirect_uri: 'http://localhost:63150/auth.html',
post_logout_redirect_uri: 'http://localhost:63150/index.html',
response_type: 'id_token token',
scope: 'openid profile UsersAPI',
silent_redirect_uri: 'http://localhost:63150/silent-renew.html',
automaticSilentRenew: true,
accessTokenExpiringNotificationTime: 4,
// silentRequestTimeout:10000,
filterProtocolClaims: true,
loadUserInfo: true
};
#Injectable()
export class AuthService {
mgr: UserManager = new UserManager(settings);
userLoadededEvent: EventEmitter<User> = new EventEmitter<User>();
currentUser: User;
loggedIn = false;
authHeaders: Headers;
constructor(private http: Http) {
this.mgr.getUser().then((user) => {
if (user) {
this.loggedIn = true;
this.currentUser = user;
this.userLoadededEvent.emit(user);
} else {
this.loggedIn = false;
}
}).catch((err) => {
this.loggedIn = false;
});
this.mgr.events.addUserLoaded((user) => {
this.currentUser = user;
this.loggedIn = !(user === undefined);
if (!environment.production) {
console.log('authService addUserLoaded', user);
}
});
this.mgr.events.addUserUnloaded((e) => {
if (!environment.production) {
console.log('user unloaded');
}
this.loggedIn = false;
});
}
}
And finally I make the call to identityserver like this:
constructor(public oidcSecurityService: AuthService) { }
ngOnInit() {
this.oidcSecurityService.mgr.signinRedirect();
}
The request which is sent looks like this:
http://localhost:8200/oauth/connect/authorize?client_id=EStudent&redirect_uri=http%3A%2F%2Flocalhost%3A63150%2Fauth.html&response_type=id_token%20token&scope=openid%20profile%20UsersAPI&state=91ea5de6886a49a997704bbdb4beda0c&nonce=295e6bf737274ea18ee2f575c93d150b

Your IdentityServer Client has a redirectUri that doesn't match that being used in the request:
http://localhost:63150/oauth.html
In the request, you use the following, which is missing the o in oauth:
http://localhost:63150/auth.html

Related

Token and JWT with dotnet Core 3.1 User Identification, localStorage.getItem("token") is null

I can not get 'token' value from Angular App. it returns null.
Please any one pinpoint what I have done wrong.
Here is the code in startup.cs
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
//Jwt Authentication
var key = System.Text.Encoding.UTF8.GetBytes(Configuration["ApplicationSettings:JWT_Secret"].ToString());
//services.AddAuthentication()
services.AddAuthentication(auth =>
{
auth.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
auth.DefaultScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(token =>
{
token.RequireHttpsMetadata = false;
token.SaveToken = true;
token.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = false,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false,
ClockSkew = TimeSpan.Zero
};
})
.AddIdentityServerJwt();
//End of Jwt Authentication
//services.AddControllersWithViews();
services.AddRazorPages();
//here is the code from login.cshtml.cs
public async Task<IActionResult> OnPostAsync(string returnUrl = null)
{
returnUrl = returnUrl ?? Url.Content("~/");
if (ModelState.IsValid)
{
// This doesn't count login failures towards account lockout
// To enable password failures to trigger account lockout, set lockoutOnFailure: true
var result = await _signInManager.PasswordSignInAsync(Input.Email, Input.Password, Input.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
//
// add JWT to create Token
{
_logger.LogInformation("Yitong Create Token.");
// logic to get token from GenerateJSONWebToken
var tokenString = GenerateJSONWebToken();
// end changed
//_logger.LogInformation(tokenString);
_logger.LogInformation("User logged in.");
return LocalRedirect(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToPage("./LoginWith2fa", new { ReturnUrl = returnUrl, RememberMe = Input.RememberMe });
}
if (result.IsLockedOut)
{
_logger.LogWarning("User account locked out.");
return RedirectToPage("./Lockout");
}
else
{
ModelState.AddModelError(string.Empty, "Invalid login attempt.");
return Page();
}
}
private string GenerateJSONWebToken()
{
var sec = Encoding.UTF8.GetBytes("1234567890123456");
var securityKey = new SymmetricSecurityKey(sec);
var credentials = new SigningCredentials(securityKey, SecurityAlgorithms.HmacSha256);
var claims = new[] {
new Claim(ClaimTypes.UserData, User.Identity.Name)
};
var token = new JwtSecurityToken(
"self", // issuer
"self", // Audience
null, // replace null with claims
expires: DateTime.Now.AddMinutes(120),
signingCredentials: credentials);
return new JwtSecurityTokenHandler().WriteToken(token);
}
here is my code in angular
to get data from sql server
the token is = null
getForeCast() {
const token = localStorage.getItem("token");
const httpOptions = {
headers: new HttpHeaders({
'Authorization': 'Bearer ' + token
})
};
return this.http.get<forecastModel.weatherForecast_custom[]>(this.url, httpOptions)
.pipe(
tap(() => console.log("HTTP request executed")),
catchError(err => {
console.log('Handling error locally and rethrowing it...', err);
return throwError(err);
}),
finalize(() => console.log("first finalize() block executed")),
)
};
Anyone please?
I don't know why I can not post this it says
It looks like your post is mostly code; please add some more details.'
This is the best I can describe the issue I have had.
Thank you

Im not able to access Response.Headers("Content-Disposition") on client even after enable CORS

Im trying to retrieve the filename of an excel file that is generated in the backend (Asp.net Core 2.2) and as you can see in the c# code, the filename is retrieved in the header of the response but from the client I cannot access to the header 'Content Disposition'
As you can see on the print bellow, in spite of the Content disposition is not present in the headers, it is present in XHR response headers
log of response.headers
XHR
I already enable de CORS policy in the backend as you can see here:
Startup.cs (ConfigureServices method)
services
.AddCors(options =>
{
options.AddPolicy(CorsAllowAllOrigins,
builder => builder.WithOrigins("*").WithHeaders("*").WithMethods("*"));
})
.AddMvc(options =>
{
options.Filters.Add(new AuthorizeFilter(authorizationPolicy));
})
.SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
services
.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
var key = Encoding.ASCII.GetBytes(jwtSecurityOptions.SecretKey);
options.Audience = jwtSecurityOptions.Audience;
options.RequireHttpsMetadata = false;
options.SaveToken = true;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
})
Startup.cs(Configure method)
app.UseAuthentication();
app.UseCors(CorsAllowAllOrigins);
app.UseMvc();
Controller
[HttpGet("{reportResultId}/exportExcelFile")]
[ProducesResponseType(StatusCodes.Status200OK)]
[ProducesErrorResponseType(typeof(void))]
[Authorize(Policy = nameof(RoleType.N1))]
public ActionResult ExportExcelFile(int reportResultId, [FromQuery]bool matchedRows, [FromQuery]ExportExcelType exportExcelType)
{
var authenticatedUser = Request.GetAuthenticatedUser();
var result = _reconciliationResultService.GetDataForExportExcelFile(reportResultId, matchedRows,authenticatedUser.UserName ,exportExcelType, authenticatedUser.UserName, authenticatedUser.Id);
if (result != null)
{
MemoryStream memoryStream = new MemoryStream(result.WorkbookContent);
var contentType = new Microsoft.Net.Http.Headers.MediaTypeHeaderValue("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
var fileStreamResult = new FileStreamResult(memoryStream, contentType)
{
FileDownloadName = result.FileName
};
fileStreamResult.FileDownloadName=result.FileName;
return fileStreamResult;
}
else
{
return null;
}
}
--------------Client ---------------------
Container.ts
exportExcelFile(matchedRows: string) {
this._reportService.exportExcelFile(matchedRows, this.reportInfo.id, this.exportExcelType).subscribe((response) => {
var filename = response.headers.get("Content-Disposition").split('=')[1]; //An error is thrown in this line because response.headers.get("Content-Disposition") is always null
filename = filename.replace(/"/g, "")
const blob = new Blob([response.body],
{ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
const file = new File([blob], filename,
{ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' });
this.exportingExcelFile = false;
this.ready = true;
saveAs(file);
});
}
ReportService.ts
exportExcelFile(matchedRows: string, reportInfoId: number, exportExcelType:ExportExcelType): Observable<any> {
const url = `${environment.apiUrls.v1}/reconciliationResult/${reportInfoId}/exportExcelFile?matchedRows=${matchedRows}&exportExcelType=${exportExcelType}`;
return this.http.get(url, { observe: 'response', responseType: 'blob' });
}
app.module.ts
import { NgModule } from '#angular/core';
import { BrowserModule } from '#angular/platform-browser';
import { HttpClientModule, HTTP_INTERCEPTORS } from '#angular/common/http';
import { AppRoutingModule } from './app.routing.module';
import { ReportModule } from './report/report.module';
import { AppComponent } from './app.component';
import { TopNavComponent } from './layout/topnav.component';
import { SideBarComponent } from './layout/sidebar.component';
import { DatePipe } from '#angular/common';
import { DynamicDialogModule } from 'primeng/dynamicdialog';
import { SharedModule } from './core/shared.module';
import { JwtInterceptor } from './core/_helpers/jwt.interceptor';
import { ErrorInterceptor } from './core/_helpers/error.interceptor';
import { LoginRoutingModule } from './login/login-routing.module';
import { LogingModule } from './login/login.module';
#NgModule({
imports: [
BrowserModule,
HttpClientModule,
SharedModule,
ReportModule,
AppRoutingModule,
DynamicDialogModule,
LogingModule,
LoginRoutingModule
],
declarations: [
AppComponent,
TopNavComponent,
SideBarComponent
],
providers: [
DatePipe,
{ provide: HTTP_INTERCEPTORS, useClass: JwtInterceptor, multi: true },
{ provide: HTTP_INTERCEPTORS, useClass: ErrorInterceptor, multi: true }
],
bootstrap: [AppComponent]
})
export class AppModule { }
```
Note: I'm implementing JWT. I don't know if it can have any impact on the headers.
Your server needs to expose the header as part of its CORS configuration, using WithExposedHeaders. Here's an example:
services
.AddCors(options =>
{
options.AddPolicy(CorsAllowAllOrigins, builder =>
builder.WithOrigins("*")
.WithHeaders("*")
.WithMethods("*")
.WithExposedHeaders("Content-Disposition"));
});
This sets the Access-Control-Expose-Headers header.
You can also use something like this before returning your response:
HttpContext.Response.Headers.Add("Access-Control-Expose-Headers", "Content-Disposition");
HttpContext.Response.Headers.Add("Content-Disposition", YOUR_VALUE);
This way, each endpoint can have a custom header exposed, without updating Startup.cs.

how to add soap header to react-native?

I use soap header in c# like this :
MeyerWebService.WebServiceEmployees tasnifWS = new MeyerWebService.WebServiceEmployees();
tasnifWS.UserDetailsValue = new UserDetails()
{
userName = "**",
password = "**"
};
But I dont know how to do this in react-native
You can use a simple module for making SOAP requests with WSSecurity
npm install react-native-soap-request --save
Example
const soapRequest = new SoapRequest({
security: {
username: 'username',
password: 'password'
},
targetNamespace: 'http://soap.acme.com/2.0/soap-access-services',
commonTypes: 'http://soap.acme.com/2.0/soap-common-types',
requestURL: soapWebserviceURL
});
const xmlRequest = soapRequest.createRequest({
'soap:ProductRegistrationRequest': {
attributes: {
'xmlns:soap': 'http://soap.acme.com/2.0/soap-access-services',
'xmlns:cmn': 'http://soap.acme.com/2.0/soap-common-types'
},
'soap:productId': {
'cmn:internalId': {
'cmn:id': productId
}
},
'soap:userId': {
'cmn:internalId': {
'cmn:id': userId
}
}
}
});
const response = await soapRequest.sendRequest();

ASP.Net Core JWT Login not setting HttpContext.User.Claims

I have the following for my login code, and another method to retrieve the user ID in another call.
// POST: /api/Account/Login
[HttpPost]
[AllowAnonymous]
public async Task<IActionResult> Login([FromBody]LoginViewModel model)
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByEmailAsync(model.Email);
if (user != null)
{
if (!await _userManager.IsEmailConfirmedAsync(user))
{
ModelState.AddModelError(string.Empty,
"You must have a confirmed email to log in.");
return BadRequest(Errors.AddErrorToModelState("Email", "You must have a confirmed email to log in.", ModelState));
}
}
var result = await _signInManager.PasswordSignInAsync(model.Email, model.Password, model.RememberMe, lockoutOnFailure: false);
if (result.Succeeded)
{
// IS THIS NEEDED? --> // HttpContext.User = await _userClaimsPrincipalFactory.CreateAsync(user);
var tokens = _antiforgery.GetAndStoreTokens(HttpContext);
var identity = _jwtFactory.GenerateClaimsIdentity(model.Email, user.Id);
_logger.LogInformation(1, "User logged in.");
var jwt = await Tokens.GenerateJwt(identity, _jwtFactory, model.Email, _jwtOptions, new JsonSerializerSettings { Formatting = Formatting.Indented });
return new OkObjectResult(jwt);
}
if (result.RequiresTwoFactor)
{
//return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl, RememberMe = model.RememberMe });
}
if (result.IsLockedOut)
{
string message = "User account locked out.";
_logger.LogWarning(2, message);
return BadRequest(Errors.AddErrorToModelState("Email", message, ModelState));
}
if (result.IsNotAllowed)
{
string message = "User account is not allowed to sign in.";
_logger.LogWarning(2, message);
return BadRequest(Errors.AddErrorToModelState("Email", message, ModelState));
}
return BadRequest(Errors.AddErrorToModelState("", "Sign in failed.", ModelState));
}
return BadRequest(Errors.AddErrorToModelState("", "", ModelState));
}
public string GetUserId()
{
string username = string.Empty;
ClaimsPrincipal principal = HttpContext.User as ClaimsPrincipal;
if (HttpContext.User != null)
{
var id = HttpContext.User.FindFirst(ClaimTypes.NameIdentifier);
if (id != null)
{
username = id.Value;
}
}
return username;
}
This is the code for GenerateJwt. (Are these fields standard? I see other fields being set in other code, such as in https://code-maze.com/authentication-aspnetcore-jwt-1/.)
public static async Task<string> GenerateJwt(ClaimsIdentity identity, IJwtFactory jwtFactory,string userName, JwtIssuerOptions jwtOptions, JsonSerializerSettings serializerSettings)
{
var response = new
{
id = identity.Claims.Single(c => c.Type == "id").Value,
auth_token = await jwtFactory.GenerateEncodedToken(userName, identity),
expires_in = (int)jwtOptions.ValidFor.TotalSeconds
}; // see GenerateEncodedToken pasted below.
return JsonConvert.SerializeObject(response, serializerSettings);
}
public async Task<string> GenerateEncodedToken(string userName, ClaimsIdentity identity)
{
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, userName),
new Claim(JwtRegisteredClaimNames.Jti, await _jwtOptions.JtiGenerator()),
new Claim(JwtRegisteredClaimNames.Iat, ToUnixEpochDate(_jwtOptions.IssuedAt).ToString(), ClaimValueTypes.Integer64),
identity.FindFirst(Helpers.Constants.Strings.JwtClaimIdentifiers.Rol),
identity.FindFirst(Helpers.Constants.Strings.JwtClaimIdentifiers.Id)
};
// Create the JWT security token and encode it.
var jwt = new JwtSecurityToken(
issuer: _jwtOptions.Issuer,
audience: _jwtOptions.Audience,
claims: claims,
notBefore: _jwtOptions.NotBefore,
expires: _jwtOptions.Expiration,
signingCredentials: _jwtOptions.SigningCredentials);
var encodedJwt = new JwtSecurityTokenHandler().WriteToken(jwt);
return encodedJwt;
}
These are the relevant lines in ConfigureServices.
// jwt wire up
// Get options from app settings
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;
configureOptions.RequireHttpsMetadata = false;
});
// api user claim policy
services.AddAuthorization(options =>
{
options.AddPolicy("ApiUser", policy => policy.RequireClaim(Constants.Strings.JwtClaimIdentifiers.Rol, Constants.Strings.JwtClaims.ApiAccess));
});
// add identity
var builder = services.AddIdentity<AppUser, AppRole>(o =>
{
// configure identity options
o.Password.RequireDigit = false;
o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false;
o.Password.RequireNonAlphanumeric = false;
o.Password.RequiredLength = 6;
})
.AddSignInManager<SignInManager<AppUser>>()
.AddEntityFrameworkStores<AppIdentityDbContext>()
.AddDefaultTokenProviders();
I'm using the following to pass my token from Angular.
private authHeader(): HttpHeaders {
let headers = new HttpHeaders();
if (isPlatformBrowser(this.platformId)) {
headers = headers.set('Content-Type', 'application/json');
let authToken = this.windowRefService.nativeWindow.localStorage.getItem('auth_token');
headers = headers.set('Authorization', authToken);
headers = headers.set('X-XSRF-TOKEN', this.getCookie('XSRF-TOKEN'));
}
return headers;
}
protected jsonAuthRequestOptions = { headers: this.authHeader() };
public loggedInAs(): Observable<string> {
return this.http.post<any>(this.baseUrl + "api/Account/GetUserId", { } , this.jsonAuthRequestOptions)
.map(data => {
if (data) {
return data;
}
return '';
})
.catch(this.handleError);
}
Does JWT authentication automatically set HttpContext.User? Or do I have to do that explicitly?
My succeeding call fails. (User has no claims, though HttpContext.User is not null.) I don't know whether the problem is with relying on HttpContext.User, or whether there is something wrong with the token generation / consumption.
Had a similar problem, Claims from token did not pass to HttpContext.User.Identity.Claims.
In my case, changing the order of adding services helped.
In startup.cs I've got now:
services.AddIdentity();
services.AddAuthentication();

Identityserver4 and API in single project

I have an IdentityServer4 asp.net-core host setup for Resource Owner Password Grant using JWT Bearer tokens and an API in a separate asp.net-core host which has my API and two Angular clients.
The Authentication and Authorization is working from my two Angular clients to my API.
Now I need to expose an API in the IdentityServer4 host so I can create users from one of the Angular clients.
I have copied my Authentication and Authorization setup from my API over to my IdentityServer4 host, however, I cannot get it to Authenticate.
In the below code, within the API, I can set a breakpoint on the jwt.Authority... line and the first call will trigger this breakpoint in my API but not in the IdentityServer4 host.
Authentication
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddJwtBearer(jwt =>
{
jwt.Authority = config.Authentication.Authority; //Breakpoint here
jwt.RequireHttpsMetadata = config.Authentication.RequireHttpsMetadata;
jwt.Audience = Common.Authorization.Settings.ServerApiName;
});
Authorization
I'm not sure if it's relevant, but I'm using role based authorization, the following is the setup for this.
var authPolicyBuilder = new AuthorizationPolicyBuilder()
.RequireRole(Common.Authorization.Settings.ServerApiRoleBasePolicyName)
.Build();
services.AddMvc(options =>
{
options.Filters.Add(new AuthorizeFilter(authPolicyBuilder));
...
services.AddAuthorization(options =>
{
options.AddPolicy(Common.Authorization.Settings.ServerApiSetupClientAdminRolePolicyName, policy =>
{
policy.RequireClaim("role", Common.Authorization.Settings.ServerApiSetupClientAdminRolePolicyName);
I've extracted the following from my logging:
What I see is that in the non-working case, I never get to the point of invoking the JWT validation (#3 in the working logs).
This is just a tiny extract of my logs, I can share them in entirety if needs be.
Working
1 Request starting HTTP/1.1 GET http://localhost:5100/packages/
(SourceContext:Microsoft.AspNetCore.Hosting.Internal.WebHost)
2 Connection id "0HLC8PLQH2NRU" started.
(SourceContext:Microsoft.AspNetCore.Server.Kestrel)
3 Request starting HTTP/1.1 GET http://localhost:5000/.well-known/openid-configuration
(SourceContext:Microsoft.AspNetCore.Hosting.Internal.WebHost)
--Truncated--
Not Working
1 Request starting HTTP/1.1 GET http://localhost:5000/users
(SourceContext:Microsoft.AspNetCore.Hosting.Internal.WebHost)
--Truncated--
Clients
new Client
{
ClientId = "setup_app",
ClientName = "Setup App",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
AccessTokenType = AccessTokenType.Jwt,
AccessTokenLifetime = 3600,
IdentityTokenLifetime = 3600,
UpdateAccessTokenClaimsOnRefresh = true,
SlidingRefreshTokenLifetime = 3600,
AllowOfflineAccess = false,
RefreshTokenExpiration = TokenExpiration.Absolute,
RefreshTokenUsage = TokenUsage.OneTimeOnly,
AlwaysSendClientClaims = true,
Enabled = true,
RequireConsent = false,
AlwaysIncludeUserClaimsInIdToken = true,
AllowedCorsOrigins = { config.CorsOriginSetupClient },
ClientSecrets =
{
new Secret(Common.Authorization.Settings.ServerApiSetupClientSecret.Sha256())
},
AllowedScopes =
{
Common.Authorization.Settings.ServerApiName,
}
},
new Client
{
ClientId = "client_app",
ClientName = "Client App",
AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
AccessTokenType = AccessTokenType.Jwt,
AccessTokenLifetime = 3600,
IdentityTokenLifetime = 3600,
UpdateAccessTokenClaimsOnRefresh = true,
SlidingRefreshTokenLifetime = 3600,
AllowOfflineAccess = false,
RefreshTokenExpiration = TokenExpiration.Absolute,
RefreshTokenUsage = TokenUsage.OneTimeOnly,
AlwaysSendClientClaims = true,
Enabled = true,
RequireConsent = false,
AlwaysIncludeUserClaimsInIdToken = true,
AllowedCorsOrigins = { config.CorsOriginSetupClient },
ClientSecrets =
{
new Secret(Common.Authorization.Settings.ServerApiAppClientSecret.Sha256())
},
AllowedScopes =
{
Common.Authorization.Settings.ServerApiName,
}
}
IdentityResources
return new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile(),
new IdentityResource(Common.Authorization.Settings.ServerApiScopeName, new []{
"role",
Common.Authorization.Settings.ServerApiSetupClientAdminRolePolicyName,
Common.Authorization.Settings.ServerApiAppClientAdminRolePolicyName,
Common.Authorization.Settings.ServerApiAppClientUserRolePolicyName,
}),
};
User
var adminUser = new ApplicationUser
{
UserName = "admin",
Email = "admin#noreply",
};
adminUser.Claims = new List<IdentityUserClaim>
{
new IdentityUserClaim(new Claim(JwtClaimTypes.PreferredUserName, adminUser.UserName)),
new IdentityUserClaim(new Claim(JwtClaimTypes.Email, adminUser.Email)),
new IdentityUserClaim(new Claim("role", Common.Authorization.Settings.ServerApiSetupClientAdminRolePolicyName)),
new IdentityUserClaim(new Claim("role", Common.Authorization.Settings.ServerApiRoleBasePolicyName)),
new IdentityUserClaim(new Claim("profileImage", $"https://robohash.org/{Convert.ToBase64String(System.Security.Cryptography.MD5.Create().ComputeHash(System.Text.Encoding.UTF8.GetBytes(adminUser.UserName)))}?set=set2"))
};
adminUser.AddRole(Common.Authorization.Settings.ServerApiSetupClientAdminRolePolicyName);
API
new ApiResource(Common.Authorization.Settings.ServerApiName, "Server API"){
ApiSecrets =
{
new Secret(Common.Authorization.Settings.ServerApiAppClientSecret.Sha256())
},
},
Look up here https://github.com/IdentityServer/IdentityServer4.Samples
Seems like it should be like:
Authentication:
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication(options =>
{
options.Authority = config.Authentication.Authority;
options.RequireHttpsMetadata = false;
options.ApiName = ServerApiName;
options.ApiSecret = ServerApiAppClientSecret;
});
Or with JWT you can try like:
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = config.Authentication.Authority;
options.RequireHttpsMetadata = false;
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = true,
ValidAudiences = new[]
{
$"{config.Authentication.Authority}/resources",
ServerApiName
},
};
});
Also, you will able to add authorization policy, like:
Authorization:
services.AddMvc(opt =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.RequireScope("api").Build();
opt.Filters.Add(new AuthorizeFilter(policy));
})
There is a MS out of the box service designed to add support for local APIs
First in IDS4 startup.cs, ConfigureServices add
services.AddLocalApiAuthentication();
Then in IDS4 config.cs in your client declaration
AllowedScopes = {
IdentityServerConstants.LocalApi.ScopeName, <<<< ---- This is for IDS4 Api access
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email
...
Still in config add the new scope to ApiScopes
new ApiScope(IdentityServerConstants.LocalApi.ScopeName)
Note
IdentityServerConstants.LocalApi.ScopeName resolves to 'IdentityServerApi'
Now in your shiny new Api located in the IDS4 project add the authorize tag to your api endpoint
[Authorize(IdentityServerConstants.LocalApi.PolicyName)]
[HttpGet]
public IEnumerable<string> Get()
{
return new string[] { "value1", "value2" };
}
Finally you need to request this new scope in your Client
Scope = "openid sig1 api1 profile email offline_access company IdentityServerApi",
Thats it
I think this could help, it's included in the community samples.

Categories

Resources