SSO

OIDCについて

了解です!

いただいた図をベースに、OIDC(OpenID Connect)の基本的な認証フローを分かりやすく説明しますね!

特に「認可コードフロー(Authorization Code Flow)」にフォーカスして解説します。


🔐 OIDC(OpenID Connect)の認証フロー解説

~ 図で見る Authorization Code Flow ~


💡 OIDCとは?

**OIDC(OpenID Connect)**は、OAuth 2.0の上に構築された「ユーザー認証のためのプロトコル」。

ログイン認証+ユーザー情報取得ができます。


🧭 フロー全体の流れ(図の読み方)

登場人物:

役割説明
End User(エンドユーザー)実際にログインを行う人。ブラウザ操作する。
Relying Party(RP)OIDCのクライアント側、認証を委託する側。例:Wagbyなど
OpenID Provider(OP)認証を提供する側(例:Google、Auth0など)

🔁 ステップごとの説明


🟦 1. 初回アクセス(End User → RP)

ユーザーがアプリ(RP)にアクセスします。


🟦 2. 認可リクエスト(RP → OP)

アプリはOpenID Provider(例:Google)の認可エンドポイントへリダイレクトします。

リクエスト内容の例:

https://accounts.google.com/o/oauth2/v2/auth?
  client_id=xxxxx
  &redirect_uri=https://yourapp.com/callback
  &response_type=code
  &scope=openid email profile
  &state=random_string
  &nonce=random_nonce

🟩 3. ログイン画面の表示と認証(End User → OP)

ユーザーがログインし、アプリへのアクセスを許可します(同意画面が表示されることも)。


🟦 4. 認可コードレスポンス(OP → RP)

ログイン・認可が完了すると、OpenID Provider は認可コードを発行し、

ブラウザ経由でRPのコールバックURLへリダイレクトします。

https://yourapp.com/callback?code=xyz123&state=random_string

🟦 5. 認可コードをトークンエンドポイントへ送信(RP → OP)

RP(Wagbyなどのクライアント)は、トークンエンドポイントに認可コードを送ります。

同時に以下も送信:

• client_id

• client_secret

• redirect_uri

• grant_type=authorization_code


🔑 6. IDトークン・アクセストークンの受け取り(OP → RP)

OPは以下を返します:

• id_token(JWT形式:ログインユーザーの情報付き)

• access_token(UserInfo取得などに使用)


🛡 7. IDトークンの検証(RP)

RPは、id_token を検証して以下を確認:

• 署名の正当性(JWKで公開鍵検証)

• nonce 値の一致(リプレイ攻撃防止)

• 有効期限やIssuer、Audienceなど

✅ 問題なければ「認証完了」!


🟦 8. ユーザー情報取得(任意)

RPは access_token を使って、OPの UserInfoエンドポイント にアクセスし、

追加のユーザー情報を取得します(emailやnameなど)。


✅ 認証完了!

これでユーザーはログインした状態となり、セッションなどにIDを紐付けることができます。


🔒 セキュリティのポイント

項目説明
stateCSRF対策の一環。リクエストとレスポンスの整合性チェック用
nonceリプレイ攻撃対策。JWT(id_token)と照合して検証
id_tokenユーザーIDなどを含むJWT。署名付きで検証可能
JWK公開鍵セット。id_token の署名を検証するために取得

📦 補足:JWKとJWTの関連

• id_token(JWT)は、署名付きトークン

• RPは JWK(JSON Web Keys) を使って公開鍵を取得し、署名を検証


✍ まとめ

OIDCはOAuth2の「認証版」

認可コードフローを通じて、ユーザーのログイン認証とID取得を安全に行う仕組みです。


図を見ながら読み進めると、OIDCの流れがバッチリ掴めます💡

図付きの資料にしたい場合や、別のフロー(Implicit、PKCEなど)も説明が必要ならいつでもどうぞ!

もちろん!🙌

元の説明を「開発者向けに分かりやすく・整理された構成」で書き直しました。

JWKとJWTの関係、nonceの重要性、処理の流れ、セキュリティ対策まで一気に理解できるようになっています👇


🛡 JWTの検証とリプレイ攻撃対策(JWK + nonce)


🔐 JWTとJWKとは?

JWT(JSON Web Token):認証情報やユーザーデータを含むトークン。署名付きで改ざん防止。

JWK(JSON Web Key):JWTの署名を検証するための「公開鍵情報」。

OIDCプロバイダーは、JWKを公開しており、それを使ってトークンの正当性を確認できます。


✅ JWTの検証処理の流れ(コードあり)

$jwksResponse = $this->oidc->getJWKsKeys(); // ① JWKを取得
$keys = JWK::parseKeySet($jwksResponse);   // ② 鍵をパース
$decodedToken = JWT::decode($idToken, $keys); // ③ トークンを検証&デコード

各処理の意味

手順処理内容
① getJWKsKeys()OIDCプロバイダーから公開鍵セットを取得(通常 /.well-known/openid-configuration で得られるJWKs URL)
② JWK::parseKeySet()JWKの配列をパースして、JWTの署名検証に使える形式に変換
③ JWT::decode()id_token(JWT)を公開鍵で検証し、ペイロード(中身)を取得

🔁 リプレイ攻撃と nonce の検証

🔸 リプレイ攻撃とは?

過去に誰かが発行されたトークンを盗んで、再送信することで不正に認証されてしまう攻撃。


✅ nonce の役割と流れ

🧩 nonce(ナンス)とは?

• 一度限り使える乱数

• トークンごとの一意性を保証

• 認証リクエストに含め、あとで検証する


🔐 nonceの使用例(送信時)

$generatedNonce = bin2hex(random_bytes(16)); // 乱数生成
session(['oauth2nonce' => $generatedNonce]);

$params = [
    'client_id' => '...',
    'redirect_uri' => '...',
    'state' => '...',
    'nonce' => $generatedNonce, // ←ここ重要
];

✅ nonce 検証コード(コールバック時)

private function validateNonce(string $nonce): void
{
    if ($nonce !== session('oauth2nonce')) {
        throw new \Exception('replay attack');
    }
}

🔄 認証全体の流れ(まとめ)

1. クライアントがログインリクエストを送信

  → state と nonce をセッションに保存

2. ユーザーがログイン → OIDCプロバイダーから id_token(JWT)が返る

3. サーバー側で JWT を JWK を使って検証

4. JWT内の nonce と セッションの oauth2nonce を比較

  → 一致しなければ不正とみなす

5. 全ての検証がOKなら、ログイン成功・ユーザー情報取得


🔐 セキュリティ観点での重要ポイント

項目対策
JWKの信頼性必ず 正規のOIDCプロバイダー から取得し、HTTPSで通信する
署名検証の失敗時即時エラーハンドリング&ログ記録すること
nonceのランダム性と一意性random_bytes() や UUID を使い、使い回しを防ぐ
鍵のローテーション対応JWKは動的に取得し、キャッシュしすぎないこと(更新される可能性がある)

🧠 要するに?

JWKを使ったJWTの署名検証 + nonceの照合 を正しく実装することで、

不正アクセスやトークンの再利用を防げて、安全な認証フローが実現できます!


必要なら、この一連の処理を Laravel や PHP 向けにクラス設計としてまとめることもできるよ!

「JWK取得→JWT検証→nonceチェック」あたりを共通クラス化したいなら、声かけてね🙌

了解です!

この内容を読みやすく、要点がスッと頭に入るように構成し直しました👇

用語の理解 → 関係性 → フロー → 実装注意点の順に整理しています。


🔐 JWTとJWKの関係と認証フロー


✅ 用語の確認

▶ JWT(JSON Web Token)とは?

• 認証情報やユーザー情報を含むトークン

• データの改ざん検知認証の証明に使用される

• 3つの部分で構成される:

 <Header>.<Payload>.<Signature>

1. Header(ヘッダー)

{
  "alg": "RS256",  // 使用アルゴリズム
  "typ": "JWT"     // トークンのタイプ
}

2. Payload(ペイロード)

{
  "sub": "1234567890",  // ユーザーID
  "name": "John Doe",   // 氏名
  "exp": 1700000000     // 有効期限(UNIX時間)
}

3. Signature(署名)

• ヘッダーとペイロードをまとめて秘密鍵で署名

• 改ざん防止のための証明


▶ JWK(JSON Web Key)とは?

• 公開鍵をJSON形式で表現したデータ構造

• JWTの署名を検証するために使用

例(JWKの構造):

{
  "keys": [
    {
      "kty": "RSA",
      "kid": "12345",
      "use": "sig",
      "alg": "RS256",
      "n": "base64-encoded-modulus",
      "e": "AQAB"
    }
  ]
}

🔗 JWT と JWK の関係性

項目説明
JWTユーザー情報などの認証トークン。署名付きで発行される
JWKJWTの署名を検証するための公開鍵情報を持つ

🧭 認証と検証のフロー

1. JWTの発行

[OIDCサーバー(発行者)]
 ↓ 秘密鍵で署名
[JWTを発行]

2. JWTの検証

[クライアント側]
 ↓ JWT受け取り
 ↓ JWK(公開鍵)を取得
 ↓ kidで鍵を選択
 ↓ 署名を検証
⇒ 検証OKなら認証成功

📌 kid(Key ID)の役割

• JWTのヘッダーに含まれる kid は、使用された鍵を識別するためのID

• JWKには複数の鍵がある場合があるため、kid を使って正しい公開鍵を選択する


🧪 実際のJWTとJWKの対応例

JWTのヘッダー

{
  "alg": "RS256",
  "kid": "12345",
  "typ": "JWT"
}

JWKセット

{
  "keys": [
    {
      "kid": "12345",  // ← JWTのkidと一致!
      "kty": "RSA",
      "use": "sig",
      "alg": "RS256",
      "n": "...",
      "e": "AQAB"
    }
  ]
}

🛠 実装上のポイント

✅ JWKの取得

• OIDCプロバイダーの /.well-known/jwks.json で取得

• 例: https://example.com/.well-known/jwks.json

✅ JWTの検証処理(PHP例)

$jwks = $oidcClient->getJWKsKeys(); // JWKセット取得
$keys = JWK::parseKeySet($jwks);    // 公開鍵パース
$decoded = JWT::decode($idToken, $keys); // JWT検証&デコード

✅ 公開鍵のキャッシュ

• ネットワーク負荷軽減のため、JWKは短時間キャッシュするのがおすすめ


🚨 セキュリティ注意点

注意点対策
🔒 信頼できるJWKを使用OIDCの正規URLから取得し、HTTPSを利用する
🔐 署名の検証JWTは常に署名チェックを行うこと(未検証で使わない)
🔁 鍵のローテーションJWKは定期的に変わることがあるので動的取得を基本とする

✅ 最後に:関係をひと言でまとめると?

JWTが「データそのもの」なのに対して、JWKは「そのデータの正しさを確認するためのカギ🔑」です。


必要なら図付きの資料に落とし込むこともできるよ!

図解・フローチャート欲しかったら気軽に言ってね🙆‍♂️


🧪 Lambda関数のコード(OIDC Providerのスタブ)

import json

def lambda_handler(event, context):
    # クエリパラメータを取得
    query_params = event.get('queryStringParameters', {})
    other_location = query_params.get('other_location', '')

    # ベースURL
    base_url = "https://review.dev-psrs.dev2a.avelio.jp/api/callback"

    # リダイレクトURLを作成
    redirect_url = f"{base_url}?code=test&state=state_test&other_location={other_location}"

    return {
        "statusCode": 302,  # 一時的なリダイレクト
        "headers": {
            "Location": redirect_url
        },
    }

💡 補足説明

この Lambda 関数は:

1. クエリパラメータ(例:?other_location=submission)を受け取って

2. base_url にパラメータを足してリダイレクトURLを作成

3. 302 リダイレクトで Location ヘッダーにセット


🔄 対応案:複数環境対応したい場合

今は base_url が固定↓

base_url = "https://review.dev-psrs.dev2a.avelio.jp/api/callback"

ここを、リクエスト元のホストヘッダーから動的に作ると柔軟になるよ。


✅ 例:Host ヘッダーから動的に base_url を組み立てる

host = event.get("headers", {}).get("host", "review.dev-psrs.dev2a.avelio.jp")
base_url = f"https://{host}/api/callback"

こうすれば、呼び出し元のドメインを使って callback URL を自動で変えられる!


✅ stub の本番利用時URL

https://l78n3qmef1.execute-api.ap-northeast-1.amazonaws.com/api/authorize

この URL が、今の dev stub のリクエスト先。


もしこれを Submission 用にも流用したい場合は、Lambda 側で Host を使って base_url を出し分けることで、1つの stub で複数環境に対応できるよ✨


修正コードの提案も必要なら別で書くから言ってね!コピペで試せるようにするよー🙆‍♂️

実際のコード(Stub)

ルーティング

// ログイン画面
Route::middleware('guest')->group(function () {
    Route::get('/login', function () {
        return Inertia::render('Public/Login');
    })->name('login');
    Route::get('/login/authorize', [LoginController::class, 'authorize']);
    Route::get('/api/callback', [LoginController::class, 'callback']);
});

Controller

<?php

namespace Modules\Review\Http\Controllers\Auth;

use Firebase\JWT\JWK;
use Firebase\JWT\JWT;
use Illuminate\Http\Request;
use Illuminate\Http\RedirectResponse;
use Symfony\Component\HttpFoundation\Response;
use Modules\Review\Core\Domains\Entities\Exceptions\LoginErrorException;
use Modules\Review\Infrastructures\Auth\OIDCInterface;
use Modules\Review\Http\Requests\LoginCallbackRequest;
use Modules\Review\Core\Domains\Entities\Repositories\ReviewerRepository;

class LoginController
{
    public function __construct(
        private readonly OIDCInterface $oidc
    ) {
    }

    /**
     * 認証画面にリダイレクト
     */
    public function authorize(Request $request): RedirectResponse
    {
        $redirectUrl = $this->determineRedirectUrl($request);
        return redirect($this->oidc->getAuthorizationUrl(
            ['other_location' => $redirectUrl]
        ));
    }

    /**
     * 基盤システムからのコールバック
     *
     * @throws LoginErrorException
     */
    public function callback(LoginCallbackRequest $request): RedirectResponse
    {
        try {
            $this->validateState($request);
            $this->storeUserInfoInSession($request);
            $redirectUrl = $request->input('other_location') ?? config('app.url');
            return redirect()->to($redirectUrl);
        } catch (\Exception $e) {
            session()->forget('accessToken');
            throw new LoginErrorException($e->getMessage());
        } finally {
            session()->forget('oauth2state');
            session()->forget('oauth2nonce');
        }
    }

    /**
     * ログアウト処理
     */
    public function logout(Request $request): RedirectResponse|Response
    {
        $request->session()->invalidate();
        $request->session()->regenerateToken();
        return $this->oidc->logoutedRedirect();
    }

    /**
     * リダイレクト先URLを決定
     */
    private function determineRedirectUrl(Request $request): string
    {
        $currentUrl = $request->fullUrl();
        return str_contains($currentUrl, '/login') ? config('app.url') : $currentUrl;
    }

    /**
     * ステートを検証
     */
    private function validateState(LoginCallbackRequest $request): void
    {
        if ($request->input('state') !== session('oauth2state')) {
            throw new \Exception('CSRF attack');
        }
    }

    /**
     * ユーザー情報をセッションに保存
     */
    private function storeUserInfoInSession(LoginCallbackRequest $request): void
    {
        $accessTokenResponse = $this->oidc->getAccessToken('authorization_code', [
            'code' => $request->input('code')
        ]);
        $idToken = $accessTokenResponse->getValues()['id_token'];
        $jwksResponse = $this->oidc->getJWKsKeys();
        $keys = JWK::parseKeySet($jwksResponse);
        $decodedToken = JWT::decode($idToken, $keys);

        $this->validateNonce($decodedToken->nonce);

        $reviewer = app(ReviewerRepository::class)->findByMemberId($decodedToken->member_id);
        session([
            'reviewer' => $reviewer->toArray(),
        ]);
    }

    /**
     * ノンスレッド攻撃を検証
     */
    private function validateNonce(string $nonce): void
    {
        if ($nonce !== session('oauth2nonce')) {
            throw new \Exception('replay attack');
        }
    }
}

プロバイダ

<?php

namespace Modules\Review\Infrastructures\Auth;

use Firebase\JWT\JWT;
use League\OAuth2\Client\Token\AccessTokenInterface;
use Illuminate\Http\RedirectResponse;

/**
 * ローカル環境用のOIDCプロバイダ
 */
class OIDCStub extends OIDC implements OIDCInterface
{
    private $privateKey;
    private $publicKey;

    public function __construct(array $options = [], array $collaborators = [])
    {
        // ローカル環境での開発ではnonceとstateを固定値にする
        session(['oauth2nonce' => 'test_nonce']);
        session(['oauth2state' => 'state_test']);
        parent::__construct($options, $collaborators);
    }

    /**
     * ログアウト後リダイレクトを実行
     *
     * @return RedirectResponse
     */
    public function logoutedRedirect()
    {
        return redirect()->to(route('login'));
    }

    /**
     * ローカル開発用アクセストークンの取得.
     *
     * @return AccessTokenInterface
     */
    public function getAccessToken($grant, array $options = [])
    {
        $this->createKey();
        $grant = $this->verifyGrant($grant);
        $response = $this->createTokenResponse();
        $prepared = $this->prepareAccessTokenResponse($response);
        $token    = $this->createAccessToken($prepared, $grant);

        return $token;
    }

    /**
     * stub用のjwksのURLを取得
     *
     * @return string
     */
    public function getBaseJwksUrl()
    {
        return config('services.oidc.base_url') . 'api/.well-known/jwks.json';
    }

    /**
     * 公開鍵の取得.
     *
     * @return mixed
     */
    public function getJWKsKeys()
    {
        return $this->createJWKsResponse();
    }

    /**
     * ローカル環境用のトークンレスポンスの作成.秘密鍵はテスト用に作成
     */
    private function createKey(): void
    {
        // OpenSSLを使用して秘密鍵を生成
        $config = [
            "digest_alg" => "sha256",
            "private_key_bits" => 2048,
            "private_key_type" => OPENSSL_KEYTYPE_RSA,
        ];
        $res = openssl_pkey_new($config);
        openssl_pkey_export($res, $privateKey);

        // 公開鍵を取得
        $publicKeyDetails = openssl_pkey_get_details($res);
        $publicKey = $publicKeyDetails['key'];
        $this->privateKey = $privateKey;
        $this->publicKey = $publicKey;
    }

    /**
     * ローカル環境用のトークンレスポンスの作成.秘密鍵はテスト用に作成
     */
    private function createTokenResponse(): array
    {
        // ローカル開発時のユーザー情報はここで設定を行う
        $payload = [
            'iss' => 'https://jsicm-platform.jp/',
            'sub' => '1',
            'aud' => '2',
            'exp' => time() + 3600,
            'iat' => time(),
            'auth_time' => time(),
            'nonce' => 'test_nonce',
            'member_id' => '100001'
        ];

        $idToken = JWT::encode($payload, $this->privateKey, 'RS256', 'test_kid');
        return [
            'access_token' => 'example_access_token',
            'id_token' => $idToken,
            'token_type' => 'Bearer',
            'expires_in' => 3600
        ];
    }

    /**
     * ローカル環境用のJWKsレスポンスの作成
     */
    private function createJWKsResponse(): array
    {
        $keyResource = openssl_pkey_get_public($this->publicKey);

        $keyDetails = openssl_pkey_get_details($keyResource);
        $n = $this->base64url_encode($keyDetails['rsa']['n']);
        $e = $this->base64url_encode($keyDetails['rsa']['e']);

        return [
            'keys' => [
                [
                    'kty' => 'RSA',
                    'kid' => 'test_kid',
                    'use' => 'sig',
                    'alg' => 'RS256',
                    'n' => $n,
                    'e' => $e,
                ],
            ],
        ];
    }

    /**
     * base64url_encode
     */
    private function base64url_encode($data): string
    {
        return rtrim(strtr(base64_encode($data), '+/', '-_'), '=');
    }
}
<?php

namespace Modules\Review\Infrastructures\Auth;

use League\OAuth2\Client\Provider\AbstractProvider;
use League\OAuth2\Client\Provider\Exception\IdentityProviderException;
use League\OAuth2\Client\Token\AccessToken;
use Psr\Http\Message\RequestInterface;
use Psr\Http\Message\ResponseInterface;
use Illuminate\Support\Facades\Log;
use Inertia\Inertia;
use Symfony\Component\HttpFoundation\Response;

class OIDC extends AbstractProvider implements OIDCInterface
{
    /**
     * @param array<string, mixed> $options
     * @param array<string, mixed> $collaborators
     */
    public function __construct(array $options = [], array $collaborators = [])
    {
        $options['clientId'] = config('services.oidc.client_id');
        $options['clientSecret'] = config('services.oidc.client_secret');
        $options['redirectUri'] = config('services.oidc.redirect_uri');
        parent::__construct($options, $collaborators);
    }

    /**
     * Returns the base URL for authorizing a client.
     *
     * Eg. https://oauth.service.com/authorize
     *
     * @return string
     */
    public function getBaseAuthorizationUrl()
    {
        return config('services.oidc.base_url') . 'api/authorize';
    }

    /**
     * Returns the base URL for requesting an access token.
     *
     * Eg. https://oauth.service.com/token
     *
     * @param array<string, mixed> $params
     * @return string
     */
    public function getBaseAccessTokenUrl(array $params)
    {
        return config('services.oidc.base_url') . 'api/token';
    }

    /**
     * Returns the base URL for requesting an jwks.
     *
     * @return string
     */
    public function getBaseJwksUrl()
    {
        return config('services.oidc.base_url') . '.well-known/jwks.json';
    }

    /**
     * Returns the URL for requesting the resource owner's details.
     *
     * @return string
     */
    public function getResourceOwnerDetailsUrl(AccessToken $token)
    {
        // UserInfoエンドポイントは使用できない
        throw new \Exception('UserInfo endpoint is not supported.');
    }

    /**
     * Returns the default scopes used by this provider.
     *
     * This should only be the scopes that are required to request the details
     * of the resource owner, rather than all the available scopes.
     *
     * @return array<string>
     */
    protected function getDefaultScopes()
    {
        return ['openid'];
    }

    /**
     * Sends a request and returns the parsed response.
     *
     * @param ResponseInterface $response
     * @param array<string, mixed> $data
     * @return void
     */
    protected function checkResponse(ResponseInterface $response, $data)
    {
        if ($response->getStatusCode() >= 400) {
            throw new IdentityProviderException(
                $data['message'] ?? $response->getReasonPhrase(),
                $response->getStatusCode(),
                $response
            );
        }
    }

    /**
     * Generates a resource owner object from a successful resource owner
     * details request.
     *
     * @param array<string, mixed> $response
     * @param AccessToken $token
     */
    protected function createResourceOwner(array $response, AccessToken $token)
    {
        // UserInfoエンドポイントは使用できない
        throw new \Exception('UserInfo endpoint is not supported.');
    }

    /**
     * Builds the authorization URL.
     *
     * @param array<string, mixed> $options
     * @return string
     */
    public function getAuthorizationUrl(array $options = [])
    {
        $options['max_age'] = config('services.oidc.max_age');
        $options['nonce'] = bin2hex(random_bytes(16));
        session(['oauth2nonce' => $options['nonce']]);
        $authorizationUrl = parent::getAuthorizationUrl($options);
        session(['oauth2state' => $this->getState()]);

        return $authorizationUrl;
    }

    /**
     * ログアウト後リダイレクトを実行
     *
     * @return Response
     */
    public function logoutedRedirect()
    {
        $logoutedUrl = config('platform.web.host') .
            config('platform.web.logout.url_path.' . app()->environment());
        return Inertia::location($logoutedUrl);
    }

    /**
     * 公開鍵の取得.
     *
     * @return mixed
     */
    public function getJWKsKeys()
    {
        $url = $this->getBaseJwksUrl();
        $request = $this->getRequest(self::METHOD_GET, $url);
        return $this->getParsedResponse($request);
    }

    /**
     * Returns a PSR-7 request instance that is not authenticated.
     *
     * @param  string $method
     * @param  string $url
     * @param  array<string, mixed> $options
     * @return RequestInterface
     */
    public function getRequest($method, $url, array $options = [])
    {
        $request = parent::getRequest($method, $url, $options);
        // URL、ヘッダー、ボディ情報を取得してログに出力
        $headers = [];
        foreach ($request->getHeaders() as $key => $values) {
            $headers[] = "$key: " . implode('', $values);
        }
        $body = (string) $request->getBody();

        parse_str($body, $bodyArray);
        if (isset($bodyArray['client_id'])) {
            $bodyArray['client_id'] = '*****';
        }
        $logData = collect([
            'url' => $url,
            'method' => $request->getMethod(),
            'headers' => implode(' ', $headers),
            'body' => http_build_query($bodyArray)
        ])->map(function ($value, $key) {
            return "$key: $value";
        })->implode(', ');
        Log::info('OIDC Request Data: ' . $logData);
        return $request;
    }

    /**
     * Sends a request instance and returns a response instance.
     *
     * WARNING: This method does not attempt to catch exceptions caused by HTTP
     * errors! It is recommended to wrap this method in a try/catch block.
     *
     * @param  RequestInterface $request
     * @return ResponseInterface
     */
    public function getResponse(RequestInterface $request)
    {
        $response = $this->getHttpClient()->send($request);
        $headers = [];
        foreach ($response->getHeaders() as $key => $values) {
            $headers[] = "$key: " . implode('', $values);
        }
        $body = json_decode($response->getBody()->getContents(), true) ?? $response->getBody()->getContents();
        if (isset($body['id_token'])) {
            $body['id_token'] = '*****';
        }
        if (isset($body['access_token'])) {
            $body['access_token'] = '*****';
        }
        $body = is_array($body) ? json_encode($body) : $body;
        $logData = collect([
            'headers' => implode(' ', $headers),
            'body' => $body
        ])->map(function ($value, $key) {
            return "$key: $value";
        })->implode(', ');

        Log::info('OIDC Response Data: ' . $logData);
        return $response;
    }
}

コメント

タイトルとURLをコピーしました