使用 Nebular 的 Twitch OAuth (Angular)

Twitch OAuth with Nebular (Angular)

我正在使用 Nebular 包为朋友创建自定义网站。我希望该用户必须使用 Twitch 帐户登录才能使用该网站,但我在使用 NbAuthModule 时遇到了一些问题。这就是我所做的

app.module.ts

import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';

import { AppRoutingModule } from './app-routing.module';
import { AppComponent } from './app.component';
import { NbThemeModule, NbLayoutModule, NbButtonModule, NbIconModule, NbSpinnerModule, NbCardModule } from '@nebular/theme';
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
import { NbEvaIconsModule } from '@nebular/eva-icons';
import { HttpClientModule } from '@angular/common/http';
import { NbAuthModule, NbAuthOAuth2Token, NbOAuth2AuthStrategy, NbOAuth2GrantType } from '@nebular/auth';
import { ChannelComponent } from './pages/channel/channel.component';
import { NbOAuth2CallbackComponent } from './pages/OAuth2/NbOAuth2Callback.component';
import { NbOAuth2LoginComponent } from './pages/OAuth2/NbOAuth2Login.component';

@NgModule({
  declarations: [
    AppComponent,
    ChannelComponent,
    NbOAuth2LoginComponent,
    NbOAuth2CallbackComponent
  ],
  imports: [
    BrowserModule,
    AppRoutingModule,
    BrowserAnimationsModule,
    NbThemeModule.forRoot({ name: 'dark' }),
    NbLayoutModule,
    NbButtonModule,
    NbIconModule,
    NbSpinnerModule,
    NbCardModule,
    NbAuthModule.forRoot({
      strategies:[
        NbOAuth2AuthStrategy.setup({
          clientId:'<cliend-id>',
          clientSecret: '<client-secret>',
          defaultMessages:['Successfully logged in'],
          name:'twitch',
          baseEndpoint:'https://id.twitch.tv/oauth2/',
          redirect:{
            success:'pages/channel',
            failure:'auth/error'
          },
          token:{
            endpoint:'token',
            grantType: NbOAuth2GrantType.AUTHORIZATION_CODE,
            class: NbAuthOAuth2Token,
          },
          authorize:{
            endpoint:'authorize',
            scope:'user_read',
            responseType: 'code',
            redirectUri:'http://localhost:4200/auth/callback'
          },
          refresh:{
            endpoint:'token',
            grantType: NbOAuth2GrantType.REFRESH_TOKEN
          }
        }),
      ]
    }),
    NbEvaIconsModule,
    HttpClientModule,
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

NbOAuth2Login.component.ts

import { Component, OnDestroy } from "@angular/core";
import { NbAuthOAuth2Token, NbAuthResult, NbAuthService } from "@nebular/auth";
import { Subject } from "rxjs";
import { map, tap, takeUntil} from 'rxjs/operators';

@Component({
    selector: 'nb-oauth2-login',
    template: `
      <button nbButton outline *ngIf="!token" (click)="login()" status="primary" routerLink="auth/login">
      <nb-icon icon="smiling-face-outline"></nb-icon>
        Login
      </button>

      <button nbButton outline *ngIf="token" (click)="logout()">
        Logout
      </button>
    `,
})

export class NbOAuth2LoginComponent implements OnDestroy {
  
    private destroy$ = new Subject<void>();
    // @ts-ignore
    token: NbAuthOAuth2Token;
    
    constructor(private authService: NbAuthService){
      this.authService.onTokenChange()
      .pipe(takeUntil(this.destroy$))
      // @ts-ignore
      .subscribe((token: NbAuthOAuth2Token) => {
          // @ts-ignore
          this.token = null;
          if (token && token.isValid()) {
            this.token = token;
          }
      });
    }
  
    login() {
      this.authService.authenticate('twitch')
        .pipe(takeUntil(this.destroy$))
        .subscribe((authResult: NbAuthResult) => {
        });
    }

    logout() {
      this.authService.logout('twitch')
        .pipe(takeUntil(this.destroy$))
        .subscribe((authResult: NbAuthResult) => {
        });
    }
  
    ngOnDestroy(): void {
      this.destroy$.next();
      this.destroy$.complete();
    }
}

NbOAuth2Callback.component.ts

import { Component, OnDestroy } from "@angular/core";
import { Router } from "@angular/router";
import { NbAuthResult, NbAuthService } from "@nebular/auth";
import { Subject } from "rxjs";
import { map, tap, takeUntil} from 'rxjs/operators';

@Component({
    selector: 'nb-playground-oauth2-callback',
    template: `
      <nb-layout>
        <nb-layout-column>
        <nb-card [nbSpinner]="true" nbSpinnerSize="giant" nbSpinnerStatus="info">
          <nb-card-body>
            Authenticating...
          </nb-card-body>
        </nb-card>
      </nb-layout-column>
      </nb-layout>
    `,
})

export class NbOAuth2CallbackComponent implements OnDestroy {
  
    private destroy$ = new Subject<void>();
  
    constructor(private authService: NbAuthService, private router: Router) {
      this.authService.authenticate('twitch')
        .pipe(takeUntil(this.destroy$))
        .subscribe((authResult: NbAuthResult) => {
          if (authResult.isSuccess() && authResult.getRedirect()) {
            this.router.navigateByUrl(authResult.getRedirect());
          }
          else{
            this.router.navigateByUrl('auth/error');
          }
        });
    }
  
    ngOnDestroy(): void {
      this.destroy$.next();
      this.destroy$.complete();
    }
  }

我注意到,在调试模式下,使用登录模块时,Token 为空,但我不知道为什么

为了解决这个问题,我在 app.module.ts

中使用以下语法添加了 2 个参数
...
 token:{
            endpoint:'token?client_secret=<client-secret>',
            grantType: NbOAuth2GrantType.AUTHORIZATION_CODE,
            class: NbAuthOAuth2Token,
            redirectUri: 'http://localhost:4200/auth/callback',
        },
...