Auth0 + Angularでの認証機能実装
Auth0を利用して、Angularで作成したアプリに認証機能を実装してみました。
Auth0のドキュメントにAngular向けのQuickStartがあるので、そのまま実装すればAuth0を利用できます。
Auth0 Angular 2+ SDK Quickstarts: Login
今回はAuth0で用意されているログイン画面の利用と認証後に扱うことが可能なユーザ情報の表示をやってみました。
ログイン
Auth0 Angular 2+ SDK Quickstarts: Login
つまずくポイントとしては、Auth0のログイン画面から戻ってきた場合にルートコンポーネントのコンストラクタが呼ばれることです。
ngOnInitメソッドは呼ばれないようです。
下記のようにルートコンポーネントで認証後の処理を行なっています。
import { Component } from '@angular/core'; import { AuthService } from './auth/auth.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent { constructor(public auth: AuthService) { auth.handleAuthentication(); } }
ユーザ情報の利用
Auth0 Angular 2+ SDK Quickstarts: User Profile
QuickStartのコードの場合、認証後に別コンポーネント上でユーザの情報を表示するという作りになっています。
実用的なパターンとしては、認証済みであれば、ヘッダー部にユーザ情報を表示するというケースがあるかなと思いますので、それを実現するように作り変えてみました。
GitHub - hi1280/auth0-angular-samples
app.component.ts
で認証及びユーザ情報の表示を行なっています。
app.component.ts
import { Component, OnInit } from '@angular/core'; import { AuthService } from './auth/auth.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'] }) export class AppComponent implements OnInit{ profile: any; constructor(public auth: AuthService) { auth.handleAuthentication(() => { this.initProfile(); }); } ngOnInit() { this.initProfile(); } logout() { this.profile = undefined; this.auth.logout(); } initProfile() { if (this.auth.userProfile) { this.profile = this.auth.userProfile; } else { if (this.auth.isAuthenticated()) { this.auth.getProfile((err: any, profile: any) => { this.profile = profile; }); } } } }
auth.service.ts
import { Injectable } from '@angular/core'; import { AUTH_CONFIG } from './auth0-variables'; import { Router } from '@angular/router'; import 'rxjs/add/operator/filter'; import * as auth0 from 'auth0-js'; @Injectable() export class AuthService { auth0 = new auth0.WebAuth({ clientID: AUTH_CONFIG.clientID, domain: AUTH_CONFIG.domain, responseType: 'token id_token', audience: `https://${AUTH_CONFIG.domain}/userinfo`, redirectUri: AUTH_CONFIG.callbackURL, scope: 'openid profile' }); userProfile: any; constructor(public router: Router) {} public login(): void { this.auth0.authorize(); } public handleAuthentication(cb: any): void { this.auth0.parseHash((err, authResult) => { if (authResult && authResult.accessToken && authResult.idToken) { window.location.hash = ''; this.setSession(authResult); cb(); this.router.navigate(['/home']); } else if (err) { this.router.navigate(['/home']); console.log(err); alert(`Error: ${err.error}. Check the console for further details.`); } }); } public getProfile(cb): void { const accessToken = localStorage.getItem('access_token'); if (!accessToken) { throw new Error('Access token must exist to fetch profile'); } const self = this; this.auth0.client.userInfo(accessToken, (err, profile) => { if (profile) { self.userProfile = profile; } cb(err, profile); }); } private setSession(authResult): void { // Set the time that the access token will expire at const expiresAt = JSON.stringify((authResult.expiresIn * 1000) + new Date().getTime()); localStorage.setItem('access_token', authResult.accessToken); localStorage.setItem('id_token', authResult.idToken); localStorage.setItem('expires_at', expiresAt); } public logout(): void { this.userProfile = undefined; // Remove tokens and expiry time from localStorage localStorage.removeItem('access_token'); localStorage.removeItem('id_token'); localStorage.removeItem('expires_at'); // Go back to the home route this.router.navigate(['/']); } public isAuthenticated(): boolean { // Check whether the current time is past the // access token's expiry time const expiresAt = JSON.parse(localStorage.getItem('expires_at')); return new Date().getTime() < expiresAt; } }
handleAuthenticationメソッドにコールバック関数を渡して、認証後にユーザ情報の取得を行なっているのが修正のポイントです。
まとめ
Auth0を利用すれば、Google、FacebookやTwitterといったソーシャルな認証プロバイダーを利用して、ユーザ情報を取得できるため、面倒な認証処理の実装がショートカットできます。
APIの保護などもできるようなので、認証にまつわる面倒ごとを色々省くことができるようです。
- 作者: 山田祥寛
- 出版社/メーカー: 技術評論社
- 発売日: 2017/08/04
- メディア: 大型本
- この商品を含むブログを見る