0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 3 years have passed since last update.

[Firebase Authentication]IDトークンをRevokeさせないユーザー情報更新

Last updated at Posted at 2020-03-17

はじめに

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されないようにしました。

もっといい方法があればぜひ教えてください!!

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?