[英]Understanding Identityserver4 with Identity (cookies/tokens, server architecture)
[英]Identity server 4 not working signin?ReturnUrl=/connect after update IdentityServer4 package from 2.2.0 to 2.4.0
昨天我已經將Identity Server 4更新到版本2.4.0-該軟件包還包括一個必須執行並應用於Indentity數據庫的遷移(它向Client表等添加了一些列)。 更新版本並部署到我們的開發服務器后,在執行請求期間將重定向重定向到登錄?ReturnUrl = / connect身份服務器返回404。前端使用oidc-client與身份服務器進行通信。 我想指出的是,在升級到2.4.0之前,一切都按預期進行。 如果我手動導航到身份服務器的登錄頁面,則無法正確加載
更新此程序包后,我找不到問題所在,並且開始感覺到與此次遷移相關的問題,即我對新添加的列執行了其添加的默認值,並且可能其中一些不包含在oidc-client中
請幫我這讓我發瘋
oidc客戶端設置
const settings: UserManagerSettings = {
authority: 'https://login.' + appConfig.config.mainDomain + '/',
client_id: environment.clientId,
client_secret: environment.clientSecret,
redirect_uri: window.location.origin + '/auth-callback',
post_logout_redirect_uri: window.location.origin + '/',
// these two will be done dynamically from the buttons clicked, but are
// needed if you want to use the silent_renew
response_type: 'id_token token',
scope: 'openid profile address test',
// this will toggle if profile endpoint is used
loadUserInfo: true,
// silent renew will get a new access_token via an iframe
// just prior to the old access_token expiring (60 seconds prior)
silent_redirect_uri: window.location.origin + '/assets/silent.html',
automaticSilentRenew: true,
// will revoke (reference) access tokens at logout time
revokeAccessTokenOnSignout: true,
// this will allow all the OIDC protocol claims to be visible in the window. normally a client app
// wouldn't care about them or want them taking up space
filterProtocolClaims: false
};
啟動文件
public void ConfigureServices(IServiceCollection services)
{
try
{
services.AddCors(options =>
{
// this defines a CORS policy called "default"
options.AddPolicy("default", policy =>
{
policy.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
});
if (windowsProvider != null && Convert.ToBoolean(windowsProvider[Constants.AuthenticationEnabled]))
{
services.Configure<IISOptions>(iis =>
{
iis.AuthenticationDisplayName = windowsProvider[Constants.AuthenticationDisplayName];
iis.AutomaticAuthentication = false;
});
}
var identityConnection = _mm.ConfManager.GetSection(Constants.DbConnections)[Constants.IdentityServerData];
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddMvc().AddMvcOptions(o => { o.Filters.Add(new GlobalExceptionFilter(_mm)); });
services.AddIdentityServer(x => { x.PublicOrigin = _mm.ConfManager[Constants.PublicOrigin]; })
.AddSigningCredential(Certificates.LoadCertificateFromStore(_mm.ConfManager[Constants.Certificate]))
.AddBgoIdpUserStore()
.AddRedirectUriValidator<AnyRedirectUriValidator>()
.AddConfigurationStore(options => { options.ConfigureDbContext = builder => builder.UseSqlServer(identityConnection, sql => sql.MigrationsAssembly(migrationsAssembly)); })
.AddOperationalStore(options => { options.ConfigureDbContext = builder => builder.UseSqlServer(identityConnection, sql => sql.MigrationsAssembly(migrationsAssembly)); });
services.AddAuthentication(options => { options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme; })
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.LoginPath = new PathString(Constants.SignIn);
options.LogoutPath = new PathString(Constants.SignOut);
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None;
});
services.AddLocalApiAuthentication();
}
catch (Exception ex)
{
_mm.LogErrMessage(ex.Message);
_mm.LogTraceException(ex, ex.Message);
}
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env, IDPUserContext idpUserContext, ConfigurationDbContext configurationDbContext, PersistedGrantDbContext persistedGrantDbContext)
{
try
{
// global policy - assign here or on each controller
app.UseCors("default");
app.UseExceptionHandler(Constants.ExceptionHandler);
app.Use(async (context, next) =>
{
if (context.Request.QueryString.HasValue)
{
if (!context.Request.Headers.ContainsKey("Authorization"))
{
var queryString = HttpUtility.ParseQueryString(context.Request.QueryString.Value);
string token = queryString.Get("access_token");
if (!string.IsNullOrWhiteSpace(token))
{
context.Request.Headers.Add("Authorization", new[] { string.Format("Bearer {0}", token) });
}
}
}
await next.Invoke();
});
app.UseIdentityServer();
app.UseAuthentication();
app.UseStaticFiles();
app.UseMiddleware(typeof(ErrorHandlingMiddleware));
app.UseMvcWithDefaultRoute();
}
這是GetClients()
public static IEnumerable < Client > GetClients() {
return new List < Client > () {
new Client {
ClientName = "JS dev client",
ClientId = "JSDevClient",
AllowedCorsOrigins = {
"http://mfm.localhost",
"https://mfm.localhost",
"https://test.test.app",
"http://test.test.app",
"https://localhost:4200",
"http://localhost:4200",
"https://test.app",
"https://*.test.app"
},
AccessTokenType = AccessTokenType.Reference,
//TODO: Not sure if we need the second screen ,check with the business
RequireConsent = false, // setting usage or not for consent screen
AccessTokenLifetime = 1200,
AuthorizationCodeLifetime = 1200,
IdentityTokenLifetime = 1200,
UpdateAccessTokenClaimsOnRefresh = true,
AllowOfflineAccess = true,
AllowAccessTokensViaBrowser = true,
ClientSecrets = {
new Secret("jsdevclient;dorgfgitvzmoygksqjpm,".Sha256())
},
AllowedGrantTypes = GrantTypes.Implicit,
RedirectUris = new List < string > {
"http://mfm.localhost/callback.html",
"http://mfm.localhost/auth-callback",
"https://mfm.localhost/auth-callback",
"https://test.test.app/assets/silent.html",
"http://test.test.app/assets/silent.html",
"https://test.test.app/auth-callback",
"http://test.test.app/auth-callback",
"https://*.test.app/assets/silent.html",
"https://*.test.app/auth-callback",
"http://localhost:4200/auth-callback",
"https://localhost:4200/auth-callback",
"http://localhost:4200/assets/silent.html",
"https://localhost:4200/assets/silent.html"
},
AllowedScopes = {
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Address,
"testapi",
},
PostLogoutRedirectUris = {
"http://mfm.localhost/index.html",
"http://mfm.localhost/",
"https://mfm.localhost/",
"https://dev-test.metaforms.app",
"http://dev-test.metaforms.app",
"http://localhost:4200",
"https://localhost:4200",
"https://test.app",
"https://test.app",
"https://*.test.app",
"https://test.metaforms.app",
"http://test.metaforms.app"
},
AlwaysIncludeUserClaimsInIdToken = true,
},
};
}
應用遷移
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<DateTime>(
name: "Created",
table: "IdentityResources",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.AddColumn<bool>(
name: "NonEditable",
table: "IdentityResources",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<DateTime>(
name: "Updated",
table: "IdentityResources",
nullable: true);
migrationBuilder.AlterColumn<string>(
name: "Value",
table: "ClientSecrets",
maxLength: 4000,
nullable: false,
oldClrType: typeof(string),
oldMaxLength: 2000);
migrationBuilder.AlterColumn<string>(
name: "Type",
table: "ClientSecrets",
maxLength: 250,
nullable: false,
oldClrType: typeof(string),
oldMaxLength: 250,
oldNullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "Created",
table: "ClientSecrets",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.AddColumn<DateTime>(
name: "Created",
table: "Clients",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.AddColumn<int>(
name: "DeviceCodeLifetime",
table: "Clients",
nullable: false,
defaultValue: 0);
migrationBuilder.AddColumn<DateTime>(
name: "LastAccessed",
table: "Clients",
nullable: true);
migrationBuilder.AddColumn<bool>(
name: "NonEditable",
table: "Clients",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<DateTime>(
name: "Updated",
table: "Clients",
nullable: true);
migrationBuilder.AddColumn<string>(
name: "UserCodeType",
table: "Clients",
maxLength: 100,
nullable: true);
migrationBuilder.AddColumn<int>(
name: "UserSsoLifetime",
table: "Clients",
nullable: true);
migrationBuilder.AlterColumn<string>(
name: "Value",
table: "ApiSecrets",
maxLength: 4000,
nullable: false,
oldClrType: typeof(string),
oldMaxLength: 2000,
oldNullable: true);
migrationBuilder.AlterColumn<string>(
name: "Type",
table: "ApiSecrets",
maxLength: 250,
nullable: false,
oldClrType: typeof(string),
oldMaxLength: 250,
oldNullable: true);
migrationBuilder.AddColumn<DateTime>(
name: "Created",
table: "ApiSecrets",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.AddColumn<DateTime>(
name: "Created",
table: "ApiResources",
nullable: false,
defaultValue: new DateTime(1, 1, 1, 0, 0, 0, 0, DateTimeKind.Unspecified));
migrationBuilder.AddColumn<DateTime>(
name: "LastAccessed",
table: "ApiResources",
nullable: true);
migrationBuilder.AddColumn<bool>(
name: "NonEditable",
table: "ApiResources",
nullable: false,
defaultValue: false);
migrationBuilder.AddColumn<DateTime>(
name: "Updated",
table: "ApiResources",
nullable: true);
migrationBuilder.CreateTable(
name: "ApiProperties",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
Key = table.Column<string>(maxLength: 250, nullable: false),
Value = table.Column<string>(maxLength: 2000, nullable: false),
ApiResourceId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_ApiProperties", x => x.Id);
table.ForeignKey(
name: "FK_ApiProperties_ApiResources_ApiResourceId",
column: x => x.ApiResourceId,
principalTable: "ApiResources",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateTable(
name: "IdentityProperties",
columns: table => new
{
Id = table.Column<int>(nullable: false)
.Annotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn),
Key = table.Column<string>(maxLength: 250, nullable: false),
Value = table.Column<string>(maxLength: 2000, nullable: false),
IdentityResourceId = table.Column<int>(nullable: false)
},
constraints: table =>
{
table.PrimaryKey("PK_IdentityProperties", x => x.Id);
table.ForeignKey(
name: "FK_IdentityProperties_IdentityResources_IdentityResourceId",
column: x => x.IdentityResourceId,
principalTable: "IdentityResources",
principalColumn: "Id",
onDelete: ReferentialAction.Cascade);
});
migrationBuilder.CreateIndex(
name: "IX_ApiProperties_ApiResourceId",
table: "ApiProperties",
column: "ApiResourceId");
migrationBuilder.CreateIndex(
name: "IX_IdentityProperties_IdentityResourceId",
table: "IdentityProperties",
column: "IdentityResourceId");
}
這是從日志
2019-08-29 10:43:47.300 +03:00 [WRN] App is started
2019-08-29 10:43:47.577 +03:00 [DBG] Login Url: /signin
2019-08-29 10:43:47.579 +03:00 [DBG] Login Return Url Parameter: ReturnUrl
2019-08-29 10:43:47.580 +03:00 [DBG] Logout Url: /signout
2019-08-29 10:43:47.580 +03:00 [DBG] ConsentUrl Url: /consent
2019-08-29 10:43:47.580 +03:00 [DBG] Consent Return Url Parameter: returnUrl
2019-08-29 10:43:47.580 +03:00 [DBG] Error Url: /home/error
2019-08-29 10:43:47.580 +03:00 [DBG] Error Id Parameter: errorId
2019-08-29 10:44:26.740 +03:00 [DBG] CORS request made for path: /.well-known/openid-configuration from origin: https://test.test.app
2019-08-29 10:44:26.767 +03:00 [DBG] Origin https://test.test.app is allowed: true
2019-08-29 10:44:26.773 +03:00 [DBG] CorsPolicyService allowed origin: https://test.test.app
2019-08-29 10:44:26.774 +03:00 [DBG] Request path /.well-known/openid-configuration matched to endpoint type Discovery
2019-08-29 10:44:26.790 +03:00 [DBG] Endpoint enabled: Discovery, successfully created handler: IdentityServer4.Endpoints.DiscoveryEndpoint
2019-08-29 10:44:26.790 +03:00 [INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryEndpoint for /.well-known/openid-configuration
2019-08-29 10:44:26.808 +03:00 [DBG] Start discovery request
2019-08-29 10:44:27.756 +03:00 [DBG] Found openid, profile, address, roles, country, subscriptionlevel, metaformsmanagerapi, metaformsbrowserapi, metaformsnavigatorapi, emailprocessorapi, servicebusapi, filestoreapi, jstest, metaformsfrontend, IdentityServerApi as all scopes in database
2019-08-29 10:44:27.856 +03:00 [DBG] Request path /connect/authorize matched to endpoint type Authorize
2019-08-29 10:44:27.861 +03:00 [DBG] Endpoint enabled: Authorize, successfully created handler: IdentityServer4.Endpoints.AuthorizeEndpoint
2019-08-29 10:44:27.861 +03:00 [INF] Invoking IdentityServer endpoint: IdentityServer4.Endpoints.AuthorizeEndpoint for /connect/authorize
2019-08-29 10:44:27.865 +03:00 [DBG] Start authorize request
2019-08-29 10:44:27.883 +03:00 [DBG] No user present in authorize request
2019-08-29 10:44:27.890 +03:00 [DBG] Start authorize request protocol validation
2019-08-29 10:44:28.224 +03:00 [DBG] JSDevClient found in database: true
2019-08-29 10:44:28.232 +03:00 [DBG] client configuration validation for client JSDevClient succeeded.
2019-08-29 10:44:28.300 +03:00 [DBG] Found openid, profile, address identity scopes in database
2019-08-29 10:44:28.353 +03:00 [DBG] Found test API scopes in database
2019-08-29 10:44:28.366 +03:00 [DBG] Found openid, profile, address identity scopes in database
2019-08-29 10:44:28.370 +03:00 [DBG] Found metaformsmanagerapi API scopes in database
2019-08-29 10:44:28.380 +03:00 [DBG] Calling into custom validator: IdentityServer4.Validation.DefaultCustomAuthorizeRequestValidator
2019-08-29 10:44:28.432 +03:00 [INF] ValidatedAuthorizeRequest
{
"ClientId": "JSDevClient",
"ClientName": "JS dev client",
"RedirectUri": "https://test.test.app/auth-callback",
"AllowedRedirectUris": ["https://*.test.app/assets/silent.html", "http://mfm.localhost/callback.html", "http://mfm.localhost/auth-callback", "https://mfm.localhost/auth-callback", "https://test.test.app/assets/silent.html", "http://test.test.app/assets/silent.html", "https://test.test.app/auth-callback", "http://test.test.app/auth-callback", "https://*.test.app/auth-callback", "http://localhost:4200/auth-callback", "https://localhost:4200/auth-callback", "http://localhost:4200/assets/silent.html", "https://localhost:4200/assets/silent.html"],
"SubjectId": "anonymous",
"ResponseType": "id_token token",
"ResponseMode": "fragment",
"GrantType": "implicit",
"RequestedScopes": "openid profile address testapi",
"State": "dffeb4fca72e43afaecb5fb5daff2348",
"UiLocales": null,
"Nonce": "8dad56338d7f40b595a9f2d91aba18d0",
"AuthenticationContextReferenceClasses": null,
"DisplayMode": null,
"PromptMode": null,
"MaxAge": null,
"LoginHint": null,
"SessionId": null,
"Raw": {
"client_id": "JSDevClient",
"redirect_uri": "https://test.test.app/auth-callback",
"response_type": "id_token token",
"scope": "openid profile address testapi",
"state": "dffeb4fca72e43afaecb5fb5daff2348",
"nonce": "8dad56338d7f40b595a9f2d91aba18d0"
},
"$type": "AuthorizeRequestValidationLog"
}
2019-08-29 10:44:28.497 +03:00 [INF] Showing login: User is not authenticated
2019-08-29 10:44:28.499+03:00 [INF] Cookies was not authenticated. Failure message: Unprotect ticket failed
2019-08-29 10:44:28.499 +03:00 [INF] Cookies was not authenticated. Failure message: Unprotect ticket failed
2019-08-29 10:44:28.499 +03:00 [INF] Cookies was not authenticated. Failure message: Unprotect ticket failed
我找到了導致問題的原因,但我不知道為什么
services.AddAuthentication(options => { options.DefaultScheme =
CookieAuthenticationDefaults.AuthenticationScheme; })
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
//options.LoginPath = new PathString(Constants.SignIn);
//options.LogoutPath = new PathString(Constants.SignOut);
options.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None;
});
在我注釋它們后,這兩行分別注釋LoginPath(/ signin)和LogoutPath(/ signout),一切都按預期進行。 請問能否提供更多信息,為什么我會遇到這種行為
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.