将 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 需要从访问令牌中读取组

如何为 OAuth2 访问令牌指定受众?

Spring Security OAuth2 不使用属性中的令牌过期值

将 curl OAuth2 令牌请求转换为 httr

Spring Boot 2 OIDC(OAuth2)客户端/资源服务器未在 WebClient 中传播访问令牌