IdentityServer4 выдает ошибку переполнения стека

#angular #asp.net-core #identityserver4 #angular-oauth2-oidc

#angular #asp.net-ядро #identityserver4 #angular-oauth2-oidc

Вопрос:

У меня есть SPA (Angular 9), и он использует сайт идентификации (.NET Core 3.1 с identity4). Оба сайта работают хорошо, однако, если пользователь надолго прекращает просмотр и пытается перейти снова, сайт идентификации выдает ошибку 503 (ошибка переполнения стека).

Это мой конфиг на Angular 9, angular-oauth2-oidc.

 // app.component.ts
// ----------------------------

export const authConfig: AuthConfig = {
  issuer: environment.authority,
  redirectUri: environment.redirectUrl,
  clientId: environment.clientId,
  scope: "openid profile offline_access",
  disablePKCE: false,
  responseType: "code",
  requireHttps: false,
  logoutUrl: environment.authority   "/Account/Logout"
}

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html'
})
export class AppComponent implements OnInit {
  title = 'app';
  public isAuthenticated: Observable<boolean>;
  public configs = null;

  constructor(
    private ouathService: OAuthService,
    private storage: SessionStorageService
  ) {
    this.ouathService.configure(authConfig);
    this.ouathService.tokenValidationHandler = new JwksValidationHandler();
  }
}

// Authguard.js
// ----------------------------

export class AuthGuard implements CanActivate {
    constructor(..) { }
    canActivate(..): boolean {
        if (this.oauthService.hasValidAccessToken()) {
            console.log("Token validado");
            let token = this.oauthService.getAccessToken();
            //...
            return true;
        } else {
            console.log("Inicia flujo");
            this.oauthService.initImplicitFlow();
            this.oauthService.loadDiscoveryDocumentAndTryLogin();   
        }
    }
}

app.module.ts
----------------------------
@NgModule({
  declarations: [..],
  imports: [
    //..
    OAuthModule.forRoot(),
    RouterModule.forRoot([
      { path: '', component: StartComponent },
      { path: 'logout', component: LoginComponent },
      { path: 'home', component: HomeComponent, canActivate: [AuthGuard, AppGuard] },      
    ])
  ],
  providers: [
    LoaderService,
    { provide: HTTP_INTERCEPTORS, useClass: TokenInterceptor, multi: true }
  ],
  bootstrap: [AppComponent]
})
 

Это моя конфигурация на сайте идентификации: .NET Core 3.1 и identity4.

 // Config
// ----------------------------
public IEnumerable<IdentityResource> GetIdentityResources() {
    return new List<IdentityResource> {
        new IdentityResources.OpenId(),
        new IdentityResources.Profile()
    };
}

public IEnumerable<Client> GetClients() {
    var client = _conf["URL_fronted"];
    return new[] {
        new Client {
            ClientId = "..",
            ClientName = "..",
            AllowedGrantTypes = GrantTypes.Code,
            RequireClientSecret = false,
            RequirePkce = true,
            RedirectUris = { $"{client}" },
            FrontChannelLogoutUri = $"{client}",
            PostLogoutRedirectUris = { $"{client}/logout" },
            AllowOfflineAccess = true,
            AllowedScopes = { "openid","profile", "offline_access" },                    
        }
    };
}

// Startup
// ----------------------------
services.AddIdentity<ApplicationUser, IdentityRole>()
  .AddEntityFrameworkStores<ApplicationDbContext>()
  .AddDefaultTokenProviders();

services.Configure<IISServerOptions>(iis =>
{
  iis.AuthenticationDisplayName = "Windows";
  iis.AutomaticAuthentication = true;
});

var confIdentity = new ConfigIdentity(Configuration);

var builder = services.AddIdentityServer(options =>
{
  options.Events.RaiseErrorEvents = true;
  options.Events.RaiseInformationEvents = true;
  options.Events.RaiseFailureEvents = true;
  options.Events.RaiseSuccessEvents = true;
})
  .AddInMemoryIdentityResources(confIdentity.GetIdentityResources())
  .AddInMemoryClients(confIdentity.GetClients())
  .AddProfileService<IdentityWithAdditionalClaimsProfileService>();
builder.AddDeveloperSigningCredential();
services.AddCors(options =>
{
  options.AddPolicy("CorsPolicy",
      builder => builder.AllowAnyOrigin()
      .AllowAnyMethod()
      .AllowAnyHeader());
});
 
 // IdentityWithAdditionalClaimsProfileService
// -----------------------------------------------

public class IdentityWithAdditionalClaimsProfileService : IProfileService
    {
        private readonly IUserClaimsPrincipalFactory<ApplicationUser> _claimsFactory;
        private readonly UserManager<ApplicationUser> _userManager;
        private ILogger<IdentityWithAdditionalClaimsProfileService> _logger;

        private ApplicationDbContext _ctx;
        public IdentityWithAdditionalClaimsProfileService(UserManager<ApplicationUser> userManager,
        IUserClaimsPrincipalFactory<ApplicationUser> claimsFactory,
        ILogger<IdentityWithAdditionalClaimsProfileService> logger,
        ApplicationDbContext context)
        {
            _userManager = userManager;
            _claimsFactory = claimsFactory;
            _logger = logger;
            _ctx = context;
        }

        public async Task GetProfileDataAsync(ProfileDataRequestContext context)
        {
            var sub = context.Subject.GetSubjectId();            
            var user = _ctx.users.Where(..).FirstOrDefault();
            if (user == null)
            {
                throw new Exception("No se encontro usuario para perfilar");
            }

            var claims = new List<Claim>();
            claims.Add(new Claim(ClaimTypes.Name, user.Names));
            claims.Add(new Claim(ClaimTypes.Email, user.Email));

            if (user.RoleId != null)
            {
                var rolGlobal = _ctx.roles.Where(..).FirstOrDefault();
                if (rolGlobal != null)
                {
                    claims.Add(new Claim(ClaimTypes.Role, rolGlobal.AzureGroup));
                }
            }

            context.IssuedClaims = claims;

        }

        public async Task IsActiveAsync(IsActiveContext context)
        {
            context.IsActive = true;
        }
    }
 

Это журнал:

 20:41:12 Debug] IdentityServer4.Hosting.EndpointRouter
Request path /.well-known/openid-configuration/jwks matched to endpoint type Discovery

[20:41:12 Debug] IdentityServer4.Hosting.EndpointRouter
Endpoint enabled: Discovery, successfully created handler: IdentityServer4.Endpoints.DiscoveryKeyEndpoint

[20:41:12 Information] IdentityServer4.Hosting.IdentityServerMiddleware
Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryKeyEndpoint for /.well-known/openid-configuration/jwks

[20:41:12 Debug] IdentityServer4.Endpoints.DiscoveryKeyEndpoint
Start key discovery request

[21:31:23 Debug] IdentityServer4.Hosting.CorsPolicyProvider
CORS request made for path: /.well-known/openid-configuration from origin: http://localhost:4200

[21:31:23 Debug] IdentityServer4.Services.InMemoryCorsPolicyService
Client list checked and origin: http://localhost:4200 is not allowed

[21:31:23 Warning] IdentityServer4.Hosting.CorsPolicyProvider
CorsPolicyService did not allow origin: http://localhost:4200

[21:31:23 Debug] IdentityServer4.Hosting.EndpointRouter
Request path /.well-known/openid-configuration matched to endpoint type Discovery

[21:31:23 Debug] IdentityServer4.Hosting.EndpointRouter
Endpoint enabled: Discovery, successfully created handler: IdentityServer4.Endpoints.DiscoveryEndpoint

[21:31:23 Information] IdentityServer4.Hosting.IdentityServerMiddleware
Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryEndpoint for /.well-known/openid-configuration

[21:31:23 Debug] IdentityServer4.Endpoints.DiscoveryEndpoint
Start discovery request

[21:31:23 Debug] IdentityServer4.Hosting.CorsPolicyProvider
CORS request made for path: /.well-known/openid-configuration/jwks from origin: http://localhost:4200

[21:31:23 Debug] IdentityServer4.Services.InMemoryCorsPolicyService
Client list checked and origin: http://localhost:4200 is not allowed

[21:31:23 Warning] IdentityServer4.Hosting.CorsPolicyProvider
CorsPolicyService did not allow origin: http://localhost:4200

[21:31:23 Debug] IdentityServer4.Hosting.EndpointRouter
Request path /.well-known/openid-configuration/jwks matched to endpoint type Discovery

[21:31:23 Debug] IdentityServer4.Hosting.EndpointRouter
Endpoint enabled: Discovery, successfully created handler: IdentityServer4.Endpoints.DiscoveryKeyEndpoint

[21:31:23 Information] IdentityServer4.Hosting.IdentityServerMiddleware
Invoking IdentityServer endpoint: IdentityServer4.Endpoints.DiscoveryKeyEndpoint for /.well-known/openid-configuration/jwks

[21:31:23 Debug] IdentityServer4.Endpoints.DiscoveryKeyEndpoint
Start key discovery request

Stack overflow.
 

Комментарии:

1. У вас есть циклический вызов где-то в службе аутентификации или охране

2. Не могли бы вы также включить исходный код IdentityWithAdditionalClaimsProfileService класса в вопрос?

3. @AndrewSilver Я включил код IdentityWithAdditionalClaimsProfileService. Сайт SPA, действительный токен, но срок его действия истек, попробуйте обновить и перенаправить на сайт идентификации для нового входа в систему, но сайт отключается.

4. 503 указывает на «служба недоступна», часто временно. Причиной вполне может быть исключение переполнения стека, в любом случае это будет ошибка в вашем коде csharp , а не на вашей интерфейсной стороне. Кроме того, насколько я могу судить, виновник кроется не в коде, который вы разместили здесь. Я не думаю, что мы сможем воспроизвести вашу проблему только с помощью того, что вы опубликовали. — Попробуйте создать минимальный образец настройки и поделиться всеми деталями здесь, если сможете? Удачи!

Ответ №1:

Моя проблема заключалась в установке Identityserver как InMemory:

Startup.cs

 var builder = services.AddIdentityServer(options =>
{
 //...
})
  .AddInMemoryIdentityResources(confIdentity.GetIdentityResources())
  .AddInMemoryClients(confIdentity.GetClients())
 

Этот параметр не работал, когда пользователь надолго прекращал просмотр. Через некоторое время сайт идентификации потерял токен клиента, и при попытке его найти он выдал ошибку stackoverflow

Есть два решения:

1.- Измените конфигурации для использования entity framework

 services.AddIdentityServer()
        // this adds the operational data from DB (codes, tokens, consents)
        .AddOperationalStore(options =>
        {
            options.ConfigureDbContext = builder =>
                builder.UseSqlServer(connectionString,
                    sql => sql.MigrationsAssembly(migrationsAssembly));

            // this enables automatic token cleanup. this is optional.
            options.EnableTokenCleanup = true;
            options.TokenCleanupInterval = 3600; // interval in seconds (default is 3600)
        });
 

2.- Клиент отправляет запрос на обновление токена

app.component.ts

 this.ouathService.refreshToken();