Open29

auth0関連

yasushi.kobayashiyasushi.kobayashi

バックエンドPHP関連

yasushi.kobayashiyasushi.kobayashi
<?php

namespace App\Service/Auth0Service;

use Auth0\SDK\API\Authentication;
use Auth0\SDK\API\Management;
use Auth0\SDK\Auth0;

final class Auth0Service
{
    private function management(): Management
    {
        $auth = $this->authentication();
        $res = $auth->client_credentials([
            'client_id' => env('AUTH0_MANAGEMENT_CLIENT_ID'),
            'client_secret' => env('AUTH0_MANAGEMENT_CLIENT_SECRET'),
            'audience' => env('AUTH0_MANAGEMENT_API_END_POINT'),
            'grant_type' => 'client_credentials',
        ]);
        return new Management(
            $res['access_token'],
            env('AUTH0_DOMAIN')
        );
    }

    private function authentication(): Authentication
    {
        return new Authentication(
            env('AUTH0_DOMAIN'),
            env('AUTH0_CLIENT_ID'),
            env('AUTH0_CLIENT_SECRET')
        );
    }

    private function auth0(): Auth0
    {
        return new Auth0([
            'domain' => env('AUTH0_DOMAIN'),
            'client_id' => env('AUTH0_CLIENT_ID'),
            'client_secret' => env('AUTH0_CLIENT_SECRET'),
            'redirect_uri' => 'http://localhost:3001/test/user',
        ]);
    }

yasushi.kobayashiyasushi.kobayashi

フロント関連

yasushi.kobayashiyasushi.kobayashi

@auth0/auth0-spa-jsauth0-js の違い

@auth0/auth0-spa-jsの方が新しくて、ユニバーサルログインが必須になる
auth0-js 古いけど既存のログインのUI変更したくないなど、ユニバーサルログインを使いたくない場合は使わざるを得ない

また、SDKの併用は可能(auth0-jsでログインするけど、token関連の処理は新しい@auth0/auth0-spa-jsを使って、トークンを取得するなど

yasushi.kobayashiyasushi.kobayashi

ログイン必須のエラーだけ握りつぶしている書き方好ましくないが、現状公式もこの書き方なので、好ましい書き方が不明。
https://github.com/auth0/auth0-spa-js#creating-the-client

isAuthenticated というメソッドがあるが、リロードすると毎回falseを返す(getTokenSilentlyをすると、trueを返すが)ので、ログイン済みかの判定に使えない

import { Auth0Client } from '@auth0/auth0-spa-js'

import { auth0ClientId, auth0Domain, auth0RedirectUrl } from '@/config'
import { pagesPath } from '@/utils/$path'

const domain: string = auth0Domain
const redirect_uri: string = auth0RedirectUrl

const auth0App = new Auth0Client({
  client_id: auth0ClientId,
  redirect_uri,
  domain,
  useRefreshTokens: true,
})

export const auth0Login = async () => {
  await auth0App.loginWithPopup({
    redirect_uri,
  })
}

export const getToken = async () => {
  try {
    const token = await auth0App.getTokenSilently()
    return token
  } catch (e: any) {
    if (e && typeof e === 'object' && e?.error !== 'login_required') {
      throw e
    }
    return null
  }
}

export const auth0logout = async () => {
  await auth0App.logout({
    returnTo: pagesPath.login.$url().pathname,
  })
}

yasushi.kobayashiyasushi.kobayashi

バックエンドnodejs

yasushi.kobayashiyasushi.kobayashi

googleログインで必要最低限片付けしたものが Auth0User なので、多少の違いはログイン方法でありそう。
clientSecret はなくても動く

import { AuthenticationClient } from 'auth0'

import { auth0ClientId, auth0ClientSecret, auth0Domain } from '@/config'

const auth0 = new AuthenticationClient({
  domain: auth0Domain,
  clientId: auth0ClientId,
  clientSecret: auth0ClientSecret,
})

export interface Auth0User {
  sub: string
  email: string
  name: string
}

export const verifyToken = async (token: string): Promise<Auth0User> => {
  const profile = await auth0.getProfile(token)
  return profile as Auth0User
}

// if (process.env.NODE_ENV === 'test') {
//   describe('verifyToken', () => {
//     it('トークンの検証', async () => {
//       const res = await verifyToken(
//         '',
//       )
//       console.log({ res })
//     })
//   })
// }
yasushi.kobayashiyasushi.kobayashi

golang api

yasushi.kobayashiyasushi.kobayashi
import (
	"context"
	"strings"

	"github.com/auth0/go-auth0/authentication"
	"github.com/morikuni/failure"
)

type Auth0User struct {
	Name  string `json:"name"`
	Email string `json:"email"`
	Sub   string `json:"sub"`
}

func decodeAth0Token(ctx context.Context, token string) (*Auth0User, error) {
	domain := "local-iesapuri-poc.jp.auth0.com"
	clientID := "C8gxrMxKAd9dNe0noQuQkDovLdcg1qRY"
	clientSecret := "evRz4UeQt8e4gCsa-uO5pbg78N_SoyPvStaibv4AQE0UvMNiJck7GXfFR54RWTEb"

	authAPI, err := authentication.New(
		ctx,
		domain,
		authentication.WithClientID(clientID),
		authentication.WithClientSecret(clientSecret), // Optional depending on the grants used
	)
	if err != nil {
		return nil, failure.Wrap(err)
	}

	user, err := authAPI.UserInfo(ctx, token)
	if err != nil {
		return nil, failure.Wrap(err)
	}

	sub := splitSub(user.Sub)
	u := &Auth0User{
		Name:  user.Name,
		Email: user.Email,
		Sub:   sub,
	}
	return u, nil
}

func splitSub(sub string) string {
	arr1 := strings.Split(sub, "|")
	return arr1[1]
}