ねぎ嫌い

始業前に学んだことを小出しに。最近はHacker Newsの人気記事をまとめてみたり。

社内システムの認証にOktaを使って楽をする

背景

  • 社内システムを使うのにアカウントの管理が面倒だった
  • ちょうどOktaを使い始める風潮があったので相乗りしたかった

前提

  • Oktaを導入している

やること

  • Oktaのアプリを登録する
  • フロントエンドでOktaのSSOを実装する
  • バックエンドでアクセストークンの検証をする

参考

  • フロントエンド (Next.js)

developer.okta.com

  • バックエンド (NestJS)

developer.okta.com

実装の一部

フロントエンド

// pages/api/auth/[...nextauth].ts

import NextAuth from 'next-auth';
import provider from 'next-auth/providers';

export default NextAuth({
  providers: [
    provider.Okta({
      clientId: process.env.OKTA_CLIENTID,
      clientSecret: process.env.OKTA_CLIENTSECRET,
      domain: process.env.OKTA_DOMAIN,
    }),
  ],
  theme: 'auto',
});

アプリを登録した際に発行されるClient ID/Client Secretを渡してあげる。 DomainはHTTPスキーマを抜いた形式 (xxx.okta.com/oauth2/$issuer)

pages/api/auth/[...nextauth].tsのパスはnext-authの指定。 NextAuth.jsに処理を以上するためには/api/auth/*の形式である必要がある。

バックエンド

基本的には参考したURLのものそのままだが、やることは以下

  • passport jsの実装
  • guardsの実装

Passport Strategyをまず実装する。

// auth/http.strategy.ts
import { HttpException, Injectable } from '@nestjs/common';
import { PassportStrategy } from '@nestjs/passport';
import { Strategy } from 'passport-http-bearer';
import { AuthService } from './auth.service';

@Injectable()
export class HttpStrategy extends PassportStrategy(Strategy) {
  constructor(private readonly service: AuthService) {
    super();
  }

  async validate(
    token: string,
    done: (error: HttpException, value: boolean | string) => any,
  ) {
    try {
      return await this.service.validateToken(token);
    } catch (error) {
      done(error, 'token is invalid');
    }
  }
}

AuthService内で実際の検証作業を実施する。 validateはAuthGuardによって呼ばれており、User等を返すとそのままリクエストに付与してくれる。

ガードしたいエンドポイントにデコレータで宣言すれば終わり。

// user.controller.ts
  @Get()
  @UseGuards(AuthGuard('bearer'))
  async getUsers(): Promise<User[]> {
    return this.service.getUsers();
  }