将 OAuth2 访问令牌配置为 typescript-angular2 客户端
Posted
技术标签:
【中文标题】将 OAuth2 访问令牌配置为 typescript-angular2 客户端【英文标题】:Configuring OAuth2 access token to typescript-angular2 client 【发布时间】:2017-10-27 18:19:25 【问题描述】:我不完全了解如何将 OAuth2 访问令牌从 Promise (oidc-client-js) 提供给使用 Swagger-CodeGen 生成的 API 代码。
提供常量值很容易,但是我如何在下面进行更改以从 oidc-client-js 获取用户的访问令牌?我想知道“正确”的方式。将此标记粘贴到全局变量中的某个位置很容易。
@NgModule(
imports: [
CommonModule,
ApiModule.forConfig(() => new Configuration(
accessToken: 'my-access-token' //this can also be a () => string function
))
],
在使用 OnInit 的普通组件中,我可以从 oidc-client 的 UserManager 实例中获取承诺中的令牌。让这两个部分组合在一起让我感到困惑。一个看起来像静态配置,另一个需要订阅单例的承诺。
this.userSubscription = this.authService.getUser().subscribe((user) =>
if (user)
this.access_token = user.access_token;
);
对我做错的事情的任何更正也将不胜感激。这是我使用 Angular 的第一个原型。
更新
在应用 Ben 的建议并花时间了解 APP_INITIALIZER(标记为实验性且 imo 记录非常稀少)之后,感觉有点矫枉过正。我以 Configuration 类的以下自定义提供程序结束,该提供程序被注入到使用 Swagger-CodeGen 生成的 TypeScript-Angular2 服务代码中:
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 生成的代码中调用,并返回最新的 jwt 以用于 HTTP 标头。感觉很干净,而且很有效。如果(以及为什么)这是解决我的问题的错误方法,请告诉我。
【问题讨论】:
你有工作样本吗? 【参考方案1】:您需要使用 APP_INITIALIZER 来引导您的 API 令牌,请查看我的回答 Pass web application context to Angular2 Service 以查看如何执行此操作的示例。
【讨论】:
【参考方案2】:我认为这是一个swagger-codegen bug,属性签名应该是
accessToken?: string | (() => Promise<string>);
或者干脆
accessToken?: (() => Promise<string>);
原因是访问令牌过期,所以每次调用都会
客户端应检查令牌是否已过期,如果已过期,则请求新的令牌(令牌刷新),这意味着 HTTP 查询,因此承诺是处理访问令牌的最佳选择。如果您检查 Firebase 的 javascript API,您会注意到 User.getIdToken()
返回一个承诺,因为它首先检查当前是否已过期,如果是,则请求一个新的。
所以我同时使用的解决方案是 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 ,
],
【讨论】:
以上是关于将 OAuth2 访问令牌配置为 typescript-angular2 客户端的主要内容,如果未能解决你的问题,请参考以下文章
Spring oauth2刷新令牌 - 无法将访问令牌转换为JSON
JHipster - OAuth2/OIDC 需要从访问令牌中读取组