使用 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',
},
...
我正在使用 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',
},
...