将 OAuth2 访问令牌配置为 typescript-angular2 客户端
Configuring OAuth2 access token to typescript-angular2 client
我不完全理解如何从承诺 (oidc-client-js) 向使用 Swagger-CodeGen 生成的 API 代码提供 OAuth2 访问令牌。
提供常量值很容易,但我如何在下面进行更改以从 oidc-client-js 获取用户的访问令牌?我想知道 "correct" 方式。将此标记粘贴在全局变量中的某个位置很容易。
@NgModule({
imports: [
CommonModule,
ApiModule.forConfig(() => new Configuration({
accessToken: 'my-access-token' //this can also be a () => string function
}))
],
在带有 OnInit 的普通组件中,我可以从 oidc-client 的 UserManager 实例中获取承诺中的令牌。让这两部分组合在一起让我感到困惑。一个好像是静态配置,另一个需要订阅单例的promise
this.userSubscription = this.authService.getUser().subscribe((user) => {
if (user) {
this.access_token = user.access_token;
}
});
对我做错的事情的任何更正也将不胜感激。这是我使用 Angular.
的第一个原型
更新
在采纳 Ben 的建议并花时间理解 APP_INITIALIZER(在我看来,它被标记为实验性的并且文档很少)之后,感觉有点矫枉过正。我以配置 class 的以下自定义提供程序结束,它被注入 TypeScript-Angular2 使用 Swagger-CodeGen:
生成的服务代码
providers: [
AuthService,
AuthGuardService,
{
provide: Configuration,
useFactory: (authSvc: AuthService) => new Configuration({accessToken: authSvc.getAccessToken.bind(authSvc)}),
deps: [AuthService],
multi: false
}
]
我更改了我的 AuthService 以在该服务上存储用户的最新 access_token。 getAccessToken()
方法从 Swagger-CodeGen 生成的代码和 returns HTTP headers 中使用的最新 jwt 调用。它感觉干净而且有效。如果(以及为什么)这是解决我问题的错误方法,请告诉我。
您需要使用 APP_INITIALIZER 到 bootstrap 您的 API 令牌,请查看我的回答 Pass web application context to Angular2 Service 以查看如何执行此操作的示例。
我认为这是一个 swagger-codegen 错误,属性 签名应该是
accessToken?: string | (() => Promise<string>);
或者干脆
accessToken?: (() => Promise<string>);
原因是访问令牌过期,所以每次调用都会进行
客户端应检查令牌是否已过期,如果已过期则请求一个新令牌(令牌刷新),这意味着 HTTP 查询,因此承诺是处理访问令牌的最佳选择。如果您检查 Firebase 的 Javascript API,您会注意到 User.getIdToken()
returns 一个承诺,因为它首先检查当前是否已过期,如果是则请求一个新的。
所以我同时使用的解决方案是Angular的HTTP拦截器:
import { Injectable } from '@angular/core';
import {
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest
} from '@angular/common/http';
import { AngularFireAuth } from '@angular/fire/auth';
import * as firebase from 'firebase/app';
import { from } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
import { environment } from '../environments/environment';
@Injectable({
providedIn: 'root'
})
export class UsersApiAuthInterceptorService implements HttpInterceptor {
constructor(private afAuth: AngularFireAuth) { }
intercept(req: HttpRequest<any>, next: HttpHandler) {
if (req.url.startsWith(environment.usersAPIBasePath) && this.afAuth.auth.currentUser) {
return from(this.afAuth.auth.currentUser.getIdToken()).pipe(mergeMap(token => {
console.log('UsersApiAuthInterceptorService got token', token);
const authReq = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
return next.handle(authReq);
}));
}
else {
return next.handle(req);
}
}
}
我不喜欢这个解决方案的地方在于它会拦截所有 HTTPClient 调用,这就是为什么我必须添加 if (req.url.startsWith(environment.usersAPIBasePath) ...
但如果您的所有 HTTPClient 调用都将发送到您的 API 你可以删除那部分条件。
这就是该应用程序的提供程序进入 app.module.ts
:
的方式
providers: [
...
{ provide: BASE_PATH, useValue: environment.usersAPIBasePath },
{ provide: HTTP_INTERCEPTORS, useClass: UsersApiAuthInterceptorService, multi: true },
],
我不完全理解如何从承诺 (oidc-client-js) 向使用 Swagger-CodeGen 生成的 API 代码提供 OAuth2 访问令牌。
提供常量值很容易,但我如何在下面进行更改以从 oidc-client-js 获取用户的访问令牌?我想知道 "correct" 方式。将此标记粘贴在全局变量中的某个位置很容易。
@NgModule({
imports: [
CommonModule,
ApiModule.forConfig(() => new Configuration({
accessToken: 'my-access-token' //this can also be a () => string function
}))
],
在带有 OnInit 的普通组件中,我可以从 oidc-client 的 UserManager 实例中获取承诺中的令牌。让这两部分组合在一起让我感到困惑。一个好像是静态配置,另一个需要订阅单例的promise
this.userSubscription = this.authService.getUser().subscribe((user) => {
if (user) {
this.access_token = user.access_token;
}
});
对我做错的事情的任何更正也将不胜感激。这是我使用 Angular.
的第一个原型更新
在采纳 Ben 的建议并花时间理解 APP_INITIALIZER(在我看来,它被标记为实验性的并且文档很少)之后,感觉有点矫枉过正。我以配置 class 的以下自定义提供程序结束,它被注入 TypeScript-Angular2 使用 Swagger-CodeGen:
生成的服务代码providers: [
AuthService,
AuthGuardService,
{
provide: Configuration,
useFactory: (authSvc: AuthService) => new Configuration({accessToken: authSvc.getAccessToken.bind(authSvc)}),
deps: [AuthService],
multi: false
}
]
我更改了我的 AuthService 以在该服务上存储用户的最新 access_token。 getAccessToken()
方法从 Swagger-CodeGen 生成的代码和 returns HTTP headers 中使用的最新 jwt 调用。它感觉干净而且有效。如果(以及为什么)这是解决我问题的错误方法,请告诉我。
您需要使用 APP_INITIALIZER 到 bootstrap 您的 API 令牌,请查看我的回答 Pass web application context to Angular2 Service 以查看如何执行此操作的示例。
我认为这是一个 swagger-codegen 错误,属性 签名应该是
accessToken?: string | (() => Promise<string>);
或者干脆
accessToken?: (() => Promise<string>);
原因是访问令牌过期,所以每次调用都会进行
客户端应检查令牌是否已过期,如果已过期则请求一个新令牌(令牌刷新),这意味着 HTTP 查询,因此承诺是处理访问令牌的最佳选择。如果您检查 Firebase 的 Javascript API,您会注意到 User.getIdToken()
returns 一个承诺,因为它首先检查当前是否已过期,如果是则请求一个新的。
所以我同时使用的解决方案是Angular的HTTP拦截器:
import { Injectable } from '@angular/core';
import {
HttpEvent, HttpInterceptor, HttpHandler, HttpRequest
} from '@angular/common/http';
import { AngularFireAuth } from '@angular/fire/auth';
import * as firebase from 'firebase/app';
import { from } from 'rxjs';
import { mergeMap } from 'rxjs/operators';
import { environment } from '../environments/environment';
@Injectable({
providedIn: 'root'
})
export class UsersApiAuthInterceptorService implements HttpInterceptor {
constructor(private afAuth: AngularFireAuth) { }
intercept(req: HttpRequest<any>, next: HttpHandler) {
if (req.url.startsWith(environment.usersAPIBasePath) && this.afAuth.auth.currentUser) {
return from(this.afAuth.auth.currentUser.getIdToken()).pipe(mergeMap(token => {
console.log('UsersApiAuthInterceptorService got token', token);
const authReq = req.clone({
setHeaders: {
Authorization: `Bearer ${token}`
}
});
return next.handle(authReq);
}));
}
else {
return next.handle(req);
}
}
}
我不喜欢这个解决方案的地方在于它会拦截所有 HTTPClient 调用,这就是为什么我必须添加 if (req.url.startsWith(environment.usersAPIBasePath) ...
但如果您的所有 HTTPClient 调用都将发送到您的 API 你可以删除那部分条件。
这就是该应用程序的提供程序进入 app.module.ts
:
providers: [
...
{ provide: BASE_PATH, useValue: environment.usersAPIBasePath },
{ provide: HTTP_INTERCEPTORS, useClass: UsersApiAuthInterceptorService, multi: true },
],