我在 Angular 项目中使用 ASP.NET Core IdentityServer4 作为 IdP 和 oidc-client 库来集成 id 服务。但是,用户登录后,在 Web 浏览器控制台中出现了两次以下错误:
VM10 vendor.js:12638 ERROR Error: Uncaught (in promise): ErrorResponse: login_required
ErrorResponse: login_required
at new e (VM10 vendor.js:49237)
at t [as _processSigninParams] (VM10 vendor.js:49237)
at t [as validateSigninResponse] (VM10 vendor.js:49237)
at VM10 vendor.js:49237
at ZoneDelegate.invoke (VM7 polyfills.js:10689)
at Object.onInvoke (VM10 vendor.js:34843)
at ZoneDelegate.invoke (VM7 polyfills.js:10688)
at Zone.run (VM7 polyfills.js:10451)
at VM7 polyfills.js:11593
at ZoneDelegate.invokeTask (VM7 polyfills.js:10723)
at resolvePromise (VM7 polyfills.js:11530)
at VM7 polyfills.js:11437
at asyncGeneratorStep (VM10 vendor.js:61578)
at _throw (VM10 vendor.js:61601)
at ZoneDelegate.invoke (VM7 polyfills.js:10689)
at Object.onInvoke (VM10 vendor.js:34843)
at ZoneDelegate.invoke (VM7 polyfills.js:10688)
at Zone.run (VM7 polyfills.js:10451)
at VM7 polyfills.js:11593
at ZoneDelegate.invokeTask (VM7 polyfills.js:10723)
我的 ASP.NET Core IdentityServer4 具有以下客户端配置:
public static IEnumerable<Client> GetClients() =>
new[]
{
new Client
{
RequireConsent = false,
ClientId = "web",
ClientName = "Abacuza Administrator",
AllowedGrantTypes = GrantTypes.Implicit,
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
IdentityServerConstants.StandardScopes.Email,
"roles",
"api"
},
RedirectUris = { "http://localhost:4200/auth-callback" },
PostLogoutRedirectUris = {"http://localhost:4200/"},
AllowedCorsOrigins = {"http://localhost:4200"},
AllowAccessTokensViaBrowser = true,
AlwaysSendClientClaims = true,
AlwaysIncludeUserClaimsInIdToken = true,
AccessTokenLifetime = 3600
}
};
在我的 Angular 前端应用程序中,我创建了以下内容AuthService:
@Injectable({
providedIn: 'root'
})
export class AuthService {
private loginChangedSubject = new Subject<boolean>();
private userManager : UserManager;
private user: User | null = null;
public loginChanged = this.loginChangedSubject.asObservable();
constructor() {
this.userManager = new UserManager(this.getUserManagerSettings());
}
public async login() {
await this.userManager.signinRedirect();
}
public async logout() {
await this.userManager.signoutRedirect();
}
public get currentUser(): User | null {
return this.user;
}
public async completeAuthentication(): Promise<void> {
const user = await this.userManager.signinRedirectCallback();
if (this.user !== user) {
this.user = user;
this.loginChangedSubject.next(this.checkUser(user));
}
}
public isAuthenticated = (): Promise<boolean> => {
return this.userManager.getUser()
.then(user => {
if (this.user !== user) {
this.loginChangedSubject.next(this.checkUser(user));
this.user = user;
}
return this.checkUser(user);
});
}
public userHasRole(role: string): boolean {
const roles: string[] = this.user == null ? [] : this.user.profile.role;
return roles.findIndex(item => item === role) >= 0;
}
public get isAdmin(): boolean {
return this.userHasRole('admin');
}
private checkUser = (user: User | null): boolean => !!user && !user.expired;
private getUserManagerSettings(): UserManagerSettings {
return {
authority: 'http://localhost:9050/',
client_id: 'web',
redirect_uri: 'http://localhost:4200/auth-callback',
post_logout_redirect_uri: 'http://localhost:4200/',
response_type: 'id_token token',
scope: 'openid profile email roles api',
filterProtocolClaims: true,
loadUserInfo: true,
automaticSilentRenew: true,
revokeAccessTokenOnSignout: true
};
}
}
由于我将 redirect_uri 配置为在 route 下auth-callback,因此我创建了以下AuthCallback组件:
@Component({
selector: 'app-auth-callback',
templateUrl: './auth-callback.component.html',
styleUrls: ['./auth-callback.component.scss']
})
export class AuthCallbackComponent implements OnInit {
constructor(private authService: AuthService,
private router: Router,
private route: ActivatedRoute) { }
async ngOnInit() {
await this.authService.completeAuthentication();
this.router.navigate(['/'], { replaceUrl: true });
}
}
当然,我将它放入路由配置中,以便auth-callback可以重定向到AuthCallbackComponent:
{ path: 'auth-callback', component: AuthCallbackComponent },
最后,我AuthService在我的一个应用程序组件中使用了:
@Component({
selector: 'app-main-side-bar',
templateUrl: './main-side-bar.component.html',
styleUrls: ['./main-side-bar.component.scss']
})
export class MainSideBarComponent implements OnInit {
public user: User | null = null;
public isAdmin: boolean = false;
constructor (private authService: AuthService) {
this.authService.loginChanged
.subscribe(authenticated => {
if (authenticated) {
this.user = this.authService.currentUser;
this.isAdmin = this.authService.isAdmin;
} else {
this.user = null;
}
});
}
ngOnInit(): void {
this.authService.isAuthenticated()
.then(_ => {
this.user = this.authService.currentUser;
this.isAdmin = this.authService.isAdmin;
});
}
}
在上面的代码中,当authService.completeAuthentication()被调用时AuthCallbackComponent,它会调用该signinRedirectCallback方法,这就是报错的地方。我只是尝试了几种方法来优化代码流,但看起来都没有奏效。我错过了什么吗?有人可以帮忙吗?
提前致谢!