You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Angular 17对接Duende Identity Server遇requireHttps错误求助

Duende Identity Server + Angular 17 认证回调 requireHttps 错误排查

我负责的项目架构为:ASP.NET Core 8 Web API 服务器、Duende Identity Server 认证服务,前端使用 Angular 17。用户在 Duende Identity Server 完成认证后,本该返回用户信息,但浏览器控制台触发 requireHttps 错误。已尝试以下方案但均未解决:

  • 在认证服务和 Angular 应用中切换 requireHttps 的 true/false 设置
  • 配置自签名 SSL 证书
  • 执行 dotnet dev-certs https --trust 命令
  • 调整 CORS 策略

浏览器报错信息

Error during login callback processing issuer must use HTTPS (with TLS), or config value for property 'requireHttps' must be set to 'false' and allow HTTP (without TLS). auth-callback.component.ts:27:14
ngOnInit auth-callback.component.ts:27
Angular 34
4429 main.ts:5
Webpack 5

问题触发于 auth-callback.component.ts 的 this.oauthService.loadDiscoveryDocumentAndTryLogin() 方法执行阶段。

相关代码文件

Program.cs

using Duende.IdentityServer.Services;
using Microsoft.EntityFrameworkCore;
using System.Security.Claims;
using IdentityModel;
using Duende.IdentityServer.Validation;
using Microsoft.AspNetCore.DataProtection;
using System.IO;

var builder = WebApplication.CreateBuilder(args);

// Configure logging 
builder.Logging.ClearProviders();
builder.Logging.AddConsole();
builder.Logging.SetMinimumLevel(LogLevel.Debug);

builder.Logging.AddDebug();

// Db context for data protection
builder.Services.AddDbContext<KeyDbContext>(options =>
{
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection"));
});
// Configure Data Protection
builder.Services.AddDataProtection()
    .PersistKeysToDbContext<KeyDbContext>()
    .SetApplicationName(builder.Configuration["DataProtection:ApplicationName"] ?? "DefaultApplicationName");

// Configure DbContext
builder.Services.AddDbContext<AppDbContext>(options =>
    options.UseSqlServer(builder.Configuration.GetConnectionString("DefaultConnection")));

// Configure IdentityServer
builder.Services.AddScoped<IUserStore, UserStore>();
builder.Services.AddScoped<IProfileService, ProfileService>();
builder.Services.AddScoped<IResourceOwnerPasswordValidator, ResourceOwnerPasswordValidator>();

builder.Services.AddIdentityServer(options =>
{
    // options.EmitStaticAudienceClaim = true;
    options.KeyManagement.Enabled = false;
})
.AddInMemoryClients(Config.Clients)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddCorsPolicyService<InMemoryCorsPolicyService>()
.AddDeveloperSigningCredential(persistKey: true, filename: "tempkey.rsa");


builder.Services.AddControllersWithViews();
builder.Services.AddRazorPages();
builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowAngularApp", policy =>
    {
        policy.WithOrigins("http://localhost:4200")
              .AllowAnyHeader()
              .AllowAnyMethod();
    });
});

var app = builder.Build();

if (!app.Environment.IsDevelopment())
{
    app.UseExceptionHandler("/Home/Error");
    app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors("AllowAngularApp");
app.UseIdentityServer();
app.UseAuthorization();
app.MapDefaultControllerRoute();
app.MapRazorPages();

InitializeDatabase(app);
app.Run();

static void InitializeDatabase(IHost app)
{
    using (var scope = app.Services.CreateScope())
    {
        var context = scope.ServiceProvider.GetRequiredService<AppDbContext>();
        context.Database.Migrate();

        // Remove existing data
        context.UserClaims.RemoveRange(context.UserClaims);
        context.Users.RemoveRange(context.Users);
        context.SaveChanges();

        if (!context.Users.Any())
        {
            var users = new List<CustomUser>
            {
                new CustomUser
                {
                    SubjectId = "1",
                    Username = "nikhil",
                    Password = "password",
                    Claims = new List<UserClaim>
                    {
                        new UserClaim { Type = JwtClaimTypes.Name, Value = "Nikhil" },
                        new UserClaim { Type = JwtClaimTypes.Email, Value = "nikhil@example.com" }
                    }
                },
                new CustomUser
                {
                    SubjectId = "2",
                    Username = "bob",
                    Password = "password",
                    Claims = new List<UserClaim>
                    {
                        new UserClaim { Type = JwtClaimTypes.Name, Value = "Bob Smith" },
                        new UserClaim { Type = JwtClaimTypes.Email, Value = "bob@example.com" }
                    }
                }
            };

            context.Users.AddRange(users);
            context.SaveChanges();
        }
    }
}

Config.cs

using Duende.IdentityServer.Models;
using Duende.IdentityServer;
using IdentityModel;
using System.Security.Claims;

public static class Config
{
    public static IEnumerable<IdentityResource> IdentityResources =>
        new IdentityResource[]
        {
            new IdentityResources.OpenId(),
            new IdentityResources.Profile(),
            new IdentityResource("email", new[] { JwtClaimTypes.Email })
        };

    public static IEnumerable<ApiScope> ApiScopes =>
        new ApiScope[]
        {
            new ApiScope("api1", "My API", new List<string> { JwtClaimTypes.Name, JwtClaimTypes.Email })
        };

    public static IEnumerable<Client> Clients =>
        new Client[]
        {
            new Client
            {
                ClientId = "ro.client",
                AllowedGrantTypes = GrantTypes.ResourceOwnerPassword,
                ClientSecrets =
                {
                    new Secret("secret".Sha256())
                },
                AllowedScopes = { "openid", "profile", "api1", "email" }
            },
            new Client
            {
                ClientId = "mvc",
                AllowedGrantTypes = GrantTypes.Code,
                RequirePkce = true,
                ClientSecrets = { new Secret("secret".Sha256()) },
                // RedirectUris = { "http://localhost:5183/signin-oidc" },
                // PostLogoutRedirectUris = { "http://localhost:5183/signout-callback-oidc" },
                RedirectUris = { 
                    "https://localhost:4200",
                    "https://localhost:4200/signin-oidc",
                    "https://localhost:4200/silent-refresh.html",
                    "https://localhost:4200/home",
                    "https://localhost:4200/auth-callback",
                    "http://localhost:4200/index.html",
                    "http://localhost:4200/silent-refresh.html"
                     }, //angular application addess added
                PostLogoutRedirectUris = { "https://localhost:4200/signout-callback-oidc" },
                AllowedScopes = new List<string> 
                {
                    IdentityServerConstants.StandardScopes.OpenId,
                    IdentityServerConstants.StandardScopes.Profile,
                    "api1",
                    "offline_access",
                    "email"
                },
                AllowOfflineAccess = true,
                AccessTokenLifetime = 40,
                AbsoluteRefreshTokenLifetime = 60,
                AllowedCorsOrigins = new List<string>
                {
                    "https://localhost:4200"
                }
            }
        };
}

auth-service.ts

import { Injectable } from '@angular/core';
import { AuthConfig, OAuthService, OAuthErrorEvent } from 'angular-oauth2-oidc';

@Injectable({
  providedIn: 'root'
})
export class AuthService {
  constructor(private oauthService: OAuthService) {
    this.configure();
    this.subscribeToOAuthEvents();
  }
  private subscribeToOAuthEvents() {
    this.oauthService.events.subscribe(event => {
      if (event instanceof OAuthErrorEvent) {
        console.error('OAuthErrorEvent:', event);
      } else {
        console.log('OAuthEvent:', event);
      }
    });
  }

  private configure() {
    const authConfig: AuthConfig = {
      issuer: 'https://localhost:7230',
      redirectUri: window.location.origin + '/signin-oidc',
      postLogoutRedirectUri: window.location.origin + '/signout-callback-oidc',
      clientId: 'mvc',
      dummyClientSecret: 'secret', // In angular-oauth2-oidc, use dummyClientSecret instead of clientSecret
      responseType: 'code',
      scope: 'openid profile email api1 offline_access',
      showDebugInformation: true,
      oidc: true,
      requestAccessToken: true,
      silentRefreshRedirectUri: window.location.origin + '/silent-refresh.html',
      useSilentRefresh: true,
      sessionChecksEnabled: true,
      timeoutFactor: 0.01,
      silentRefreshTimeout: 10,
      disableAtHashCheck: true,
      requireHttps: true,
      loginUrl: 'https://localhost:7230/connect/authorize', // Set the login URL
      logoutUrl: 'https://localhost:7230/connect/endsession', // Set the logout URL
    };

    this.oauthService.configure(authConfig);
    this.oauthService.setupAutomaticSilentRefresh();
    this.oauthService.showDebugInformation = true;
    this.oauthService.loadDiscoveryDocumentAndTryLogin().then(() => {
      console.log('Discovery document loaded and try login completed');
      if (this.oauthService.hasValidAccessToken()) {
        console.log('Access token is valid');
      } else {
        console.log('No valid access token found');
      }
    }).catch(err => {
      console.error('Error loading discovery document and trying login', err);
    });
  }

  login() {
    console.log('Initiating login flow');
    this.oauthService.initCodeFlow();
  }

  logout() {
    console.log('Logging out');
    this.oauthService.logOut();
  }

  get identityClaims() {
    return this.oauthService.getIdentityClaims();
  }

  get accessToken() {
    return this.oauthService.getAccessToken();
  }
}

auth-callback.ts

import { Component, OnInit } from '@angular/core';
import { OAuthService } from 'angular-oauth2-oidc';
import { Router } from '@angular/router';

@Component({
  selector: 'app-auth-callback',
  template: `<p>Loading...</p>`,
})
export class AuthCallbackComponent implements OnInit {

  constructor(private oauthService: OAuthService, private router: Router) {}

  ngOnInit() {
    this.oauthService.loadDiscoveryDocumentAndTryLogin().then(_ => {
      console.log('Discovery document loaded and try login completed');
      if (this.oauthService.hasValidAccessToken()) {
        console.log('Access token is valid');
        console.log('Access Token:', this.oauthService.getAccessToken());
        console.log('ID Token:', this.oauthService.getIdToken());
        console.log('Identity Claims:', this.oauthService.getIdentityClaims());
        this.router.navigate(['/Home']); // Redirect to Home component after successful login
      } else {
        console.error('No valid access token found');
        this.router.navigate(['/']); // Redirect back to login on failure
      }
    }).catch(err => {
      console.error('Error during login callback processing', err);
      this.router.navigate(['/']); // Redirect back to login on error
    });
  }
}

内容的提问来源于stack exchange,提问作者Scrappy Coco

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.22 06:59:51