はじめに
Firebase Authenticationはドキュメントはかなり充実しているが、
実務で足りない箇所があったため、備忘録的に残しておきます。
実装はGoとAngularで書かれています。
IDトークンのRevokedをチェックする仕組み
フロント側でAuthorizationヘッダーにIDトークンを追加して、
APIのmiddlewareにてAuthoraizationヘッダーを検証します。
auth.ts
@Injectable({
providedIn: 'root'
})
export class Auth {
constructor(public afAuth: AngularFireAuth) { }
// getIdToken IDトークン
getIdToken(): Observable<string> {
return this.afAuth.idToken;
}
}
http.token.interceptor.ts
@Injectable()
export class HttpTokenInterceptor implements HttpInterceptor {
constructor(private auth: Auth) {}
intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> {
return this.auth.getToken().pipe(
switchMap((token: string) => {
if (token) {
const req = request.clone({
// tokenがあるならAuthorizationヘッダーに追加
setHeaders: {
Authorization: `Bearer ${ token }`
}
});
return next.handle(req);
}
// tokenがなければAuthorizationヘッダーなし
return next.handle(request);
})
).pipe(tap(() => {}
, async (err: any) => {
// エラーだったらログアウト
})
);
}
}
middleware.go
package middleware
func checkUser(next echo.HandlerFunc) echo.HandlerFunc {
return func(c echo.Context) error {
req := c.Request()
// Authorizationヘッダーを取得
token := req.Header.Get("Authorization")
if token == "" {
return next(c)
}
// IDトークンを取得
bearerToken := strings.TrimPrefix(token, "Bearer ")
ctx := context.Background()
// VerifyIDTokenAndCheckRevokedにてRevokedされているかをチェック
_, err := a.Authenticator.Client.VerifyIDTokenAndCheckRevoked(ctx, bearerToken)
if err != nil {
return err
}
// エラーがなければnext
return next(c)
}
}
Admin SDKを用いてパスワード、メールアドレスを変更した場合
ユーザーの情報はAdmin SDKの UpdateUser
にて更新することができます。
ただし、これを行うとIDトークンがRevokedされるため VerifyIDTokenAndCheckRevoked
でエラーとなります
update.go
import "firebase.google.com/go/auth"
// UpdateUser()を用いてユーザー情報を更新すると、IDトークンはRevokedされる
_, err = *auth.Client.UpdateUser(ctx, uid, (&auth.UserToUpdate{}).Password(password))
if err != nil {
return nil, err
}
reauthenticateWithCredentialを使う
クライアント側でユーザーにpasswordを入れてもらうことで、ユーザーの情報を更新できます。
セキュリティ上重要な操作を行う場合は、直前に認証を行っている必要があります。 → こちら
auth.ts
@Injectable({
providedIn: 'root'
})
export class Auth {
constructor(public afAuth: AngularFireAuth) { }
// updatePassword update current firebase user password
updatePassword(password: string): Observable<any> {
const user = this.afAuth.auth.currentUser;
return from(user.updatePassword(password));
}
}
constructor(private auth: Auth) {}
updateMethod() {
const msg = '現在のパスワードを入力';
const password = prompt(msg);
if (password === null) {
return;
}
const user = this.auth.afAuth.auth.currentUser;
const credential = firebase.auth.EmailAuthProvider
.credential(user.email, password);
from(user.reauthenticateWithCredential(credential)).pipe(
mergeMap(() => {
return this.auth.updatePassword(password);
})
).subscribe(res => {
// 完了処理
})
}
まとめ
Firebase Admin SDKを用いてIDトークンがRevokedされるのはわからなくはないが、
それでログアウトされるのもユーザー体験的に嫌なので、クライアント側からパスワードを変更して
IDトークンがRevokedされないようにしました。
もっといい方法があればぜひ教えてください!!