🚀

WorkOS.comとかいう認証サービスを掘り下げてみる

に公開

workos.comとは何者なのか

  • シングルサインオン (SSO): Okta、Azure AD、Google Workspace などの ID プロバイダーと統合できる。
  • ディレクトリ同期: 企業のディレクトリ (SCIM、LDAP など) と同期し、ユーザー管理を簡素化。
  • 監査ログ: セキュリティやコンプライアンスのための詳細なログ管理。
  • 多要素認証 (MFA): 追加のセキュリティレイヤーを簡単に実装可能。

ローカルDBで認証を管理したくない人向け、といってもwebアプリでは当然ユーザーエントリーを管理する必要はあります。

気になる料金

https://workos.com/pricing

WorkOSの価格プランまとめ

基本サービスと価格プラン

サービス 料金 機能 ポイント
AuthKit (ユーザー管理 + 認証) 無料 (最大100万ユーザー) ソーシャル認証 (Google, Facebook, etc.)、多要素認証 (MFA)、役割ベースのアクセス制御 (RBAC) 無料枠が広く、スタートアップや小規模サービスに最適
Custom Domains (カスタムドメイン) $99/月 認証画面 (AuthKit)、管理ポータル、メール送信ドメインをカスタマイズ可能 エンタープライズ向けのブランディング強化。ただし基本的に企業や何やかんやの案件でworkos.comが表示されているのは通常問題になるのでこの値段は要検討
Single Sign-On (SSO) $125/接続 (月額) エンタープライズ向け SAML & OIDC 認証、Okta, Azure AD, Google Workspace などのIDプロバイダー対応 接続ごとの課金、ボリュームディスカウントあり
Directory Sync (SCIMディレクトリ同期) $125/接続 (月額) SCIMによる自動ユーザー管理 (作成・削除・権限管理)、Active Directory, Okta, Google Workspace などと同期 人事・ITシステムと統合し、管理の手間を削減
Fine-Grained Authorization (詳細な権限管理) 無料 APIベースの権限管理、シンプルなスキーマ言語でカスタマイズ可能 独自の権限管理が必要な場合に無料で使える
Audit Logs (監査ログ) $5/組織 (月額) SIEM連携 & イベントログエクスポート 企業ごとに課金されるため、B2B SaaS向け

特に気になるのはカスタムドメインであるはずで月額99ドルである。認証の画面がわけわからんworkos.comに転送されるのをユーザーがどう感じるかを考えてみた時にまずここで採用/不採用が分かれるはずだ。


これが嫌なら99ドル/月を払うしかないと考えると自分で実装した方がいいんじゃないかという事に当然なるでしょう

SSOやディレクトリ同期まで考える人はそれなりに中〜大規模展開を考えているはずなのでまあこの額は飲めるのかもしれないなあという気はする。

サインインしてみよう

workosへのサインインもまたworkosであり...

ログイン後のdashboard

現在登録されているユーザーの数とかが見られる

  • Single Sign-On
  • Directory Sync
  • Audit Logs

に関してはoptionalというか応用の機能なのでここでは取り上げない。

authkit

認証フロー

AuthKitの認証方法

AuthKitでは、デフォルトで「メール + パスワード認証」が有効

サポートされている認証方法

  • Single Sign-On (SSO)
  • Email + Password (デフォルトで有効)
  • Social Login(ソーシャルログイン)
  • Multi-Factor Authentication (MFA)
  • Magic Auth (マジックリンク認証)

これらを独自で作る必要がなくなるので、まあ便利といえば便利なはず

各種サンプル

https://workos.com/docs/user-management/example-apps

こんな感じで提供されている

クイックスタート

WorkOS APIキー & クライアントID

これらの情報は、WorkOSダッシュボードで取得できる。

User Managementの有効化

すると

ここでは Use AuthKit for a customized, pre-built, and hosted authentication UI.を利用してみる

ある程度外観をセットアップできるがこれはskipする

これは適当にcontinueすると次

キーとかを控える。

お使いの言語がサポートするライブラリーのinstall方法を示してくるがまあ次

ここで認証エンドポイントを設定する必要がある。localhostでもいいが開発するにあたってはこれを設定する必要がある。なおlaravel12のstarter kitでは /authenticate でやっている

これで完成

サンプルプログラムの実行

ここではphpをネタにしているのでphpのものを展開してみることにしよう。php-authkit-exampleをcloneしてくる

git clone https://github.com/workos/php-authkit-example
cd php-authkit-example
cp .env.example .env
composer install

設定する

.env
WORKOS_CLIENT_ID=
WORKOS_API_KEY=
WORKOS_REDIRECT_URI=
WORKOS_COOKIE_PASSWORD=

もし控えるのを忘れた場合は WORKOS_CLIENT_IDWORKOS_API_KEYAPI Keysから取れる

WORKOS_REDIRECT_URIRedirectsメニューからセットできる。適当にセットしてはだめでこのURIに含まれている値以外は拒否される

public/index.php
<?php
$request = $_SERVER['REQUEST_URI'];

// Simple router
switch ($request) {
  case '/':
  case '':
    require __DIR__ . '/views/index.php';
    break;
  case str_starts_with($request, '/callback'):
    require __DIR__ . '/views/callback.php';
    break;
  case '/account':
    require __DIR__ . '/views/account.php';
    break;
  case '/signout':
    require __DIR__ . 'signout.php';
    break;
  default:
    http_response_code(404);
    echo '404 page not found';
    break;
}

というソースコードからわかるように、ここではhttp//yourserver/callbackがredirect URIになる

WORKOS_COOKIE_PASSWORDはとりあえずセットしなくてもok

起動してみる

php -S 0.0.0.0:8000 -t public

とかで起動すると

こんなのが表示されてくるはずなので後は適当に認証する。

認証が成功するとAccountから情報が取得できる

ログアウトの設定を正しく行わないと

こんな感じになる。

これはこの辺でセットする

ちなみにメール認証はなんだか割とgmailの迷惑メールに入りがちで微妙にあぶないかも...まあ今テスト用で適当にしているからなのかもしれないが。

organizations

初期段階でTest Organizationsは構成されており、Add userを押すことで追加できる

し、あるいはInviteすることもできる

ロールや

Settingsを見ておこう

特にSettingsにおいてはメールのドメインに応じて自動的にorganizationに追加される事を示唆している。

で、organizationはどう使うのか

SSO(シングルサインオン)でも使われるのだが、それは有料なのと面倒くさいのでちょっと置いとくとして、基本的にはロールとかパーミッションを割り当てるために使うんだと思われる。

というわけで https://workos.com/docs/user-management/roles-and-permissions を見ていく

organizationのロールとパーミッション

  • Admin:users:manage, settings:edit
  • Member:projects:view, tasks:edit
  • Viewer:projects:view

こんな感じのAuthorizationを提供してくる

設定

これはRoles & Permissionで行うのだが、割と自由にセットできるようだ。たとえばpermissionの設定

Roleの設定

ここで既にMemberAdminというロールが定義されている

ただし組織ごとのroleというのもありorg-から初まるslugを持っている

やってみよう

ここで改めてTest Organizationに所属したユーザーをみると、既にMemberになっている

ここでEdit RoleをするとMemberかAdminかを選択できる。これは先程のRoles & Permissionでみたのから理解できるはずだ。

Organizationのroleとは

とかやると

このようにenvironment roles他に別のAdminが誕生しており、そのslugはorg-adminとなっている

すると非常にわかり辛いのであるが

こんな感じで組織のAdminとWorkOS環境のAdminが出来る。ただしこれはわかり辛いにも程があるのでシステムロールと組織ロールはうまいこと管理しやすいロール名で行う必要があるという事になるね。

現状

組織のAdminを削除し、システムのAdminを割り当てた

これは

widgets:users-table:manageという権限を持っている。これはどういう風に扱われるのだろうかという言になりますな

どういう風に情報が取得されるのかということ

先程のサンプルプログラムを見ると

public/session.php
@@ -69,7 +69,8 @@ function setSessionCookie($data)
   // The refresh token is sensitive as it allows you to login the user again.
   // Therefore we encrypt it before storing in the cookie
   $encryptedSession = encrypt(json_encode($data), $_ENV['WORKOS_COOKIE_PASSWORD']);
-
+  print"<pre>";
+  print_r($data); exit;
   setcookie('wos-session', $encryptedSession, 0, '/', '', false, true);
 }

とかしてみると

Array
(
    [refresh_token] => I3rUXrrjj3hA28jwqeP1japyn
    [access_token] => eyJhbGciOiJS....................
    [user] => Array
        (
            [object] => user
            [id] => user_01JPVVJ2TA7EK6E6431DX1YFG9
            [email] => catatsumuri@gmail.com
            [firstName] => 
            [lastName] => 
            [emailVerified] => 1
            [profilePictureUrl] => https://workoscdn.com/images/v1/BcmIW_yE08vMHSHNN5ZiO3Ozke3wCAqpc7kuCLxSnq8
            [lastSignInAt] => 2025-03-24T00:07:59.779Z
            [createdAt] => 2025-03-21T08:01:52.445Z
            [updatedAt] => 2025-03-24T00:07:59.779Z
            [externalId] => 
            [metadata] => 
        )
)

とまあこんな感じで表示される。つまりroleとかpermissionとかはこれに含まれない(ので存在しない$user->roleを参照している本プログラムは正直おそらく若干バギーではある、あんま深く検証してないのでPRを送る余裕はないが)

roleやpermissionを取得

これはJWT経由で行われる。プログラムの冒頭で

public/session.php
<?php

require_once 'shared.php';

use Firebase\JWT\JWT;
use Firebase\JWT\JWK;
use GuzzleHttp\Client;

$jwksUrl = $userManagement->getJwksUrl($_ENV['WORKOS_CLIENT_ID']);

// Create a new HTTP client
$client = new Client();

try {
  // Fetch the JWKS data
  $response = $client->request('GET', $jwksUrl);
  $jwksJSON = $response->getBody()->getContents();
  $jwks = json_decode($jwksJSON, true);
} catch (\Exception $e) {
  error_log('Error fetching JWKS: ' . $e->getMessage());
}
// ...

このような形で $jwks 変数で定義されているので、これを利用する。classになってないのでglobalするしかないでしょうな。

public/session.php
@@ -70,6 +70,14 @@ function setSessionCookie($data)
   // Therefore we encrypt it before storing in the cookie
   $encryptedSession = encrypt(json_encode($data), $_ENV['WORKOS_COOKIE_PASSWORD']);

+  // Decode the access token to get the session data
+  global $jwks;
+  $keys = JWK::parseKeySet($jwks);
+  $decoded = JWT::decode($data['access_token'], $keys);
+  print"<pre>";
+  print_r($data);
+  print_r($decoded);
+  exit;

そうすると

Array
(
    [refresh_token] => XyFNZsGlGC4hpD6gxm09lzci2
    [access_token] => eyJhbGciOiJSUzI1NiIsImtpZC...........
    [user] => Array
        (
            [object] => user
            [id] => user_01JPVVJ2TA7EK6E6431DX1YFG9
            [email] => catatsumuri@gmail.com
            [firstName] => 
            [lastName] => 
            [emailVerified] => 1
            [profilePictureUrl] => https://workoscdn.com/images/v1/BcmIW_yE08vMHSHNN5ZiO3Ozke3wCAqpc7kuCLxSnq8
            [lastSignInAt] => 2025-03-24T00:26:07.644Z
            [createdAt] => 2025-03-21T08:01:52.445Z
            [updatedAt] => 2025-03-24T00:26:07.644Z
            [externalId] => 
            [metadata] => 
        )

)
stdClass Object
(
    [iss] => https://api.workos.com
    [sub] => user_01JPVVJ2TA7EK6E6431DX1YFG9
    [sid] => session_01JQ2RNQYN8D7ZGC14283ZBR63
    [jti] => 01JQ2RNRNMKACGKZGGKW2FW70S
    [org_id] => org_01JPVRZWRAGV25Y6DD98Y2AV9X
    [role] => admin
    [permissions] => Array
        (
            [0] => widgets:users-table:manage
        )

    [exp] => 1742776268
    [iat] => 1742775968
)

このような形で取得できるわけだ。

つまり

rolepermissionは情報として取得できるんだけれども、それをどのようにして使うかはprogram次第ということになる。これはspatie/laravel-permissionなどと組合せるといいのかもしれないので、次回starterに組込まれているworkos連携をさらに深く掘ってみよう。

productionアクセスとか案件ごとの使い勝手

結構わかり辛いと思うのだけど現在はstagingというレベルになっている。

プロダクションにすると課金情報を求められる。つまり、stagingレベルで使えるのは無料の機能だけという事でproductionにすれば有償の機能も使えるということなんだろう

とはいえ、productionとstagingで認証情報をがらっと切り替えられるのかというとあやしいので、結局、案件ごとにアカウントを取得する必要があるのかもしれない、し、もっと言えば、開発用のリソースもこれまた別途必要である気がする。リダイレクトURIはいくつかセットできるとはいえ大抵本番用のユーザープールと開発用では別に分かれる事になるはずだし。

さらにはドメインの設定もあるので、やっぱりどう考えても1案件ごとに1アカウントを設定するのが正しい使い方なんだろうと思う

まとめ

まあ単純にGoogleだの何だので認証したい場合には使えそうだ。それ以外の情報はプログラムでうまいこと扱えるかどうかにかかっていますね。案件で使う場合はカスタムドメインの $99/月 というなかなか豪快な値段設定をどう考えるか次第であるように思える。

Discussion