はじめに
OIDC RP を実装する場合以下のようなことをする必要があります。
※ 認可コードグラントです。
- ディスカバリ構成の管理
- 認可エンドポイントの生成
- トークンエンドポイントへのアクセス
- トークンの検証
etc ...
OIDC はカッチリ仕様が決まっていますし、そこまで複雑な実装にもならないと思いますが、ライブラリがあるなら頼りたいものです。
本記事では Go で OIDC RP をライブラリを用いて実装するにあたり候補となるライブラリを調査し実際に使ってみたのでご紹介します。
ライブラリ
調べたところ候補となるライブラリは以下の2つがありました。
(ほかにもあったら教えていただきたいです。)
どちらもライセンスは Apache-2.0 license
です。
本記事執筆で参照しているそれぞれのライブラリのバージョンは
です。
実装フロー
今回ライブラリを使ってみるにあたり実装したフローは以下となります。
説明のため、一部パラメータなどを省略しているので正確なものではありません。
ご了承ください。
Go によるサーバーでの Relying Party の実装なので nonce
や PKCE
は利用しません。
(あと、この方がよりシンプルに説明できるのかなと思っています。)
OpenAPI でエンドポイントを定義して利用します。
サーバーの実装をサボるために ogen
による自動生成を活用しています。
準備
実際に動作確認を実施するにあたり Google と連携します。
こちらのドキュメントを参考にプロジェクトを用意しています。
coreos/go-oidc
coreos/go-oidc
は以下に示すように example
が用意されています。
パッケージインポート
import(
"github.com/coreos/go-oidc/v3/oidc"
"golang.org/x/oauth2"
)
初期化処理
一連の処理にて oidc.Provider
と oauth2.Config
という構造体が必要となります。
ctx := context.Background()
provider := oidc.NewProvider(ctx, "issuer")
config := &oauth2.Config{
ClientID: "client_id",
ClientSecret: "client_secret",
Endpoint: provider.Endpoint(),
RedirectURL: "redirect_url",
Scopes: []string{oidc.ScopeOpenID}
}
認可エンドポイント生成処理
// GET /auth へのアクセス
endpoint := config.AuthCodeURL("state")
// 上記で生成したエンドポイントにリダイレクトされるようにクライアントに返す
トークン取得処理
// GET /callback へのアクセス
token, _ := config.Exchange(ctx, "code")
idToken, _ := token.Extra("id_token").(string)
IDトークン検証処理
if _, err := provider.Verifier(&oidc.Config{
ClientID: "client_id"
}).Verify(ctx, idToken); err != nil {
panic(err)
}
zitadel/oidc
zitadel/oidc
でも以下に示すように example
が用意されています。
こちらのサンプルコードでは処理を http.HandleFunc()
へと渡す形式で実装されています。
が、ogen
などのライブラリを活用して実装する場合だと参考になりません。
コードを漁っていると他の方法でも OIDC RP としての処理を記述できることがわかったのでそちらの方法で実装します。
パッケージインポート
import (
"github.com/zitadel/oidc/v3/pkg/client/rp"
"github.com/zitadel/oidc/v3/pkg/oidc"
)
初期化処理
一連の処理にて構造体 rp.RelyingParty が必要となります。
ctx := context.Background()
provider, _ := rp.NewRelyingPartyOIDC(
ctx,
"issuer",
"client_id",
"client_secret",
"redirect_uri",
[]string{"openid"},
)
認可エンドポイント生成処理
// GET /auth へのアクセス
endpoint := rp.AuthURL("state", provider)
// 上記で生成したエンドポイントにリダイレクトされるようにクライアントに返す
トークン取得処理
// GET /callback へのアクセス
token, _ := rp.CodeExchange[*oidc.IDTokenClaims](ctx, "code", provider)
idToken, _ := token.Extra("id_token").(string) // coreos と同じ処理
IDトークン検証処理
if _, err := rp.VerifyIDToken[*oidc.IDTokenClaims](ctx, idToken, provider.IDTokenVerifier()); err != nil {
panic(error)
}
おわりに
どちらのライブラリを使っても省エネルギーで OIDC RP の実装ができるなと確認できました。
正直どちらも使用感は変わらなかったですが、zitadel
のほうが rp.RelyingParty
という構造体の管理で一連の処理が記述できる点は良いと感じました。
zitadel/oidc
に以下のような記載があったりするので個人的にはこちらのライブラリを使っていこうと思います。
Easy to use OpenID Connect client and server library written for Go and certified by the OpenID Foundation
複数プロバイダーや複数のコールバックURIを管理するときの実装をいい感じにするためにはどうしようかなと迷っています ...
今回実装したコードは以下に置いておきます。ご参考まで。