Documentation
¶
Overview ¶
Package authorization は認可エンドポイント(/authorize)の処理を担う。
認可要求の解析・検証、Authorization Transaction の状態機械、Interaction App との 契約(Begin / Complete / Reject / Resume)、PKCE の検証、Authorization Code の発行を 1 つの文脈として扱う(docs/adr/0003-interaction-contract.md、docs/adr/0007-package-layout.md)。
対応する仕様:
- RFC 6749 §3.1 Authorization Endpoint、§4.1.1 Authorization Request、 §4.1.2 Authorization Response、§4.1.2.1 Error Response
- RFC 7636 §4.3 code_challenge の送信、§4.4 サーバー側の保持
- RFC 8707 §2.1 認可要求の resource パラメータ(設定と Client の許可範囲で検証し、 範囲外は invalid_target。Code に引き継ぐ)
- RFC 9207 認可レスポンスの iss パラメータ
- RFC 9700 §4.1 redirect_uri、§4.7 CSRF 対策(PKCE / state)
- OIDC Core §3.1.2.1 Authentication Request(nonce, prompt, max_age, login_hint, ...)、 §3.1.2.6 Authentication Error Response
永続化の規約: Transaction と AuthorizationCode は公開フィールドの構造体だが、 構築はこのパッケージの Service が行い、状態遷移は Store.TransitionTransaction の CAS で原子的に行う。Status フィールドへの直接代入は Adapter の復元に限る。
Index ¶
- Constants
- Variables
- func ValidCodeChallenge(s string) bool
- func ValidCodeVerifier(s string) bool
- func VerifyS256(challenge, verifier string) bool
- type AuthorizationCode
- type Error
- type Outcome
- type Patch
- type Rejection
- type Request
- type Service
- func (s *Service) Begin(ctx context.Context, params url.Values, browserBinding string) (*Transaction, error)
- func (s *Service) Complete(ctx context.Context, txnID string, out Outcome) (string, error)
- func (s *Service) Reject(ctx context.Context, txnID string, rej Rejection) (string, error)
- func (s *Service) Resume(ctx context.Context, txnID, resumeToken, browserBinding string) (string, error)
- func (s *Service) Transaction(ctx context.Context, id string) (*Transaction, error)
- type Status
- type Store
- type Transaction
Examples ¶
Constants ¶
const ( CodeInvalidRequest = "invalid_request" CodeAccessDenied = "access_denied" CodeUnsupportedResponseType = "unsupported_response_type" CodeInvalidScope = "invalid_scope" CodeServerError = "server_error" // CodeInvalidTarget は resource パラメータが不正、または設定・Client の許可範囲にない // Resource Server を指している(RFC 8707 §2)。 CodeInvalidTarget = "invalid_target" CodeInteractionRequired = "interaction_required" CodeLoginRequired = "login_required" CodeConsentRequired = "consent_required" CodeRequestNotSupported = "request_not_supported" CodeRequestURINotSupported = "request_uri_not_supported" )
RFC 6749 §4.1.2.1 と OIDC Core §3.1.2.6 のエラーコード。
const ( DefaultTransactionTTL = 10 * time.Minute MaxTransactionTTL = time.Hour DefaultCodeTTL = time.Minute MaxCodeTTL = 10 * time.Minute // RFC 6749 §4.1.2 )
期限の既定値と上限(ADR-0003 §5)。
const CodeChallengeMethodS256 = "S256"
CodeChallengeMethodS256 は唯一サポートする code_challenge_method(RFC 7636 §4.2、RFC 9700 §2.1.1)。 "plain" は受け付けない。
const ResumeTokenPrefix = "rs_"
ResumeTokenPrefix は Resume Token の接頭辞。
Variables ¶
var ( // ErrTransactionNotFound は Txn が存在しない(404)。 ErrTransactionNotFound = errors.New("authorization: transaction not found") // ErrTransactionExpired は Txn が期限切れ(410)。 ErrTransactionExpired = errors.New("authorization: transaction expired") // ErrTransactionNotPending は既に Complete / Reject 済み(409)。 ErrTransactionNotPending = errors.New("authorization: transaction is not pending") // ErrInvalidOutcome は Outcome / Rejection の内容が契約に反する(400)。詳細は fmt で付加される。 ErrInvalidOutcome = errors.New("authorization: invalid outcome") // ErrResumeDenied は Resume を拒否した(ResumeToken・ブラウザ結合の不一致、未完了、再利用)。 // 攻撃者に区別を与えないため、理由は分けない。エラーページを描き、Client へはリダイレクトしない。 ErrResumeDenied = errors.New("authorization: resume denied") )
Interaction API(Complete / Reject / Resume)向けのエラー。HTTP 層はこれらをステータスに写す。
Functions ¶
func ValidCodeChallenge ¶
ValidCodeChallenge は code_challenge が RFC 7636 §4.2 の構文に従うかを返す。 S256 の値は base64url(パディングなし)の 43 文字で、unreserved 文字の部分集合である。
func ValidCodeVerifier ¶
ValidCodeVerifier は code_verifier が RFC 7636 §4.1 の構文 (43〜128 文字の unreserved 文字 [A-Za-z0-9-._~])に従うかを返す。
func VerifyS256 ¶
VerifyS256 は BASE64URL(SHA256(code_verifier)) == code_challenge を定数時間で検証する (RFC 7636 §4.6)。
Example ¶
ExampleVerifyS256 は PKCE の S256 検証(RFC 7636 §4.6)を、RFC 7636 Appendix B の値で確かめる。 エンジンは plain を受け付けないので、Client は常に BASE64URL(SHA256(code_verifier)) を送る。
package main
import (
"fmt"
"github.com/u-masato/auth-engine/authorization"
)
func main() {
const (
verifier = "dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk"
challenge = "E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM"
)
fmt.Println(authorization.VerifyS256(challenge, verifier))
fmt.Println(authorization.VerifyS256(challenge, "wrong-verifier-wrong-verifier-wrong-verifier"))
}
Output: true false
Types ¶
type AuthorizationCode ¶
type AuthorizationCode struct {
Hash string
TxnID string
ClientID string
GrantID string
// FamilyID は、この Code の交換で作られる Token Family の ID をあらかじめ確定したもの。
// Code の再利用を検知したとき、この Family を失効させる(RFC 6749 §4.1.2)。
FamilyID string
Subject string
RedirectURI string
RedirectURIExplicit bool
Scopes []string
CodeChallenge string
CodeChallengeMethod string
Nonce string
// Resource は認可要求の resource をそのまま引き継いだもの(ADR-0008 §3)。
// Code 交換で発行する Access Token の Audience はこの部分集合になる(RFC 8707 §2.2)。
Resource []string
AuthenticatedAt time.Time
AMR []string
ACR string
Claims map[string]any
CreatedAt time.Time
ExpiresAt time.Time
ConsumedAt time.Time // ゼロ値なら未消費
}
AuthorizationCode は Resume 時に発行する単回使用の Code(RFC 6749 §4.1.2)。 Code の平文は保存せず、SHA-256 の Hash をキーにする。
func (*AuthorizationCode) Consumed ¶
func (c *AuthorizationCode) Consumed() bool
Consumed は消費済みかを返す。
type Error ¶
type Error struct {
Code string
Description string
RedirectURI string
State string
// Cause はログ用の内部原因。Client には返さない。
Cause error
}
Error は認可エンドポイントのプロトコルエラー。
RedirectURI が空のときは client_id 不明または redirect_uri 不一致であり、 RFC 6749 §4.1.2.1 により Client へリダイレクトしてはならない。呼び出し側はエラーページを描く。 RedirectURI が非空なら RedirectURL で Client 向けのエラー応答 URL を組み立てる。
func (*Error) RedirectURL ¶
RedirectURL は Client へのエラー応答 URL を返す(RFC 6749 §4.1.2.1、RFC 9207 の iss 付き)。 Redirectable でなければ空文字を返す。
func (*Error) Redirectable ¶
Redirectable は Client へリダイレクトして返してよいエラーかを返す。
type Outcome ¶
type Outcome struct {
// Subject はエンジンにとって不透明なユーザー識別子。
Subject string
// AuthenticatedAt は認証時刻。max_age の検証と ID Token の auth_time に使う。
AuthenticatedAt time.Time
// GrantedScopes は要求 scope の部分集合。
GrantedScopes []string
// AMR / ACR は ID Token にそのまま載せる(OIDC Core §2)。
AMR []string
ACR string
// Claims は ID Token / UserInfo 用のスナップショット(ADR-0006 §3)。
Claims map[string]any
}
Outcome は Interaction App が Complete で伝える成功結果(ADR-0003 §3 (5))。
type Rejection ¶
type Rejection struct {
// Error は RFC 6749 §4.1.2.1 / OIDC Core §3.1.2.6 のエラーコード。
Error string
Description string
}
Rejection は Interaction App が Reject で伝える失敗結果。
type Request ¶
type Request struct {
ClientID string
// RedirectURI は登録情報と照合済みの、実際に使う redirect URI。
RedirectURI string
// RedirectURIExplicit は要求に redirect_uri パラメータが含まれていたか。
// RFC 6749 §4.1.3 により、含まれていた場合はトークン要求でも同じ値を要求する。
RedirectURIExplicit bool
Scopes []string
State string
// PKCE(RFC 7636)。Method は常に "S256"。
CodeChallenge string
CodeChallengeMethod string
// OIDC Core §3.1.2.1。Nonce は ID Token に転記する。他はエンジンが解釈しない。
Nonce string
Prompt []string
MaxAge time.Duration
HasMaxAge bool
LoginHint string
UILocales string
ACRValues string
// Resource は RFC 8707 §2.1 の resource パラメータ。Engine の設定と Client の許可範囲で
// 検証済みで、resource.Normalize の正規形・重複なし。要求になければ空(ADR-0008 §2、§3)。
Resource []string
}
Request は検証済みの認可要求パラメータ。 エンジンが解釈するもの(redirect_uri, scope, PKCE, nonce)と、 Interaction App へ透過するヒント(prompt, max_age, login_hint, ...)の両方を持つ。
type Service ¶
type Service struct {
Clients client.Resolver
Store Store
Grants grant.Store
Clock clock.Clock
// Resources は認可要求の resource パラメータで指定できる Resource Server の集合(RFC 8707 §2.1)。
// nil なら空集合で、resource 付きの要求はすべて invalid_target になる。
Resources *resource.Set
// Issuer は認可サーバーの識別子(RFC 8414 の issuer)。認可応答の iss に使う(RFC 9207)。
Issuer string
// TransactionTTL / CodeTTL は 0 なら既定値。上限の検査は Engine の設定検証で行う。
TransactionTTL time.Duration
CodeTTL time.Duration
}
Service は認可エンドポイントのユースケース群。フィールドは Engine が配線する。
func (*Service) Begin ¶
func (s *Service) Begin(ctx context.Context, params url.Values, browserBinding string) (*Transaction, error)
Begin は /authorize の要求を検証して Transaction を作る(ADR-0003 §3 (1)(2))。
browserBinding はブラウザ結合 Cookie の値(平文)。HTTP 層が既存 Cookie を読むか新規発行して渡す。 失敗は *Error で返し、Redirectable なら Client へリダイレクトして返す。
func (*Service) Complete ¶
Complete は Interaction App からの成功結果を記録し、Resume Token の平文を返す(ADR-0003 §3 (5))。
Outcome の検証: subject 非空、authenticated_at 非ゼロ、granted_scopes が要求 scope の 空でない部分集合、max_age 要求時は認証が新しいこと。
func (*Service) Resume ¶
func (s *Service) Resume(ctx context.Context, txnID, resumeToken, browserBinding string) (string, error)
Resume はブラウザが Resume URL で戻ってきたときに Code を発行し、Client へのリダイレクト URL を返す (ADR-0003 §3 (7)(8))。
失敗のうち ErrTransactionNotFound / ErrTransactionExpired / ErrResumeDenied は Client へリダイレクトせずエラーページを描く。Txn が rejected なら、Client 向けの エラー応答 URL を返す(これは正常な戻り値であり err は nil)。
書き込みの順序は fail-closed にする: まず Txn を consumed に遷移させ(CAS)、その後に Grant と Code を作る。途中で失敗しても Txn は消費済みで、Code は存在しない。
func (*Service) Transaction ¶
Transaction は存在し期限内の Transaction を返す。Interaction App が UI を描くための GET /transactions/{id} と、Complete / Reject / Resume の前提確認に使う(ADR-0003 §3 (4))。 存在しなければ ErrTransactionNotFound、期限切れなら ErrTransactionExpired を返す。
type Status ¶
type Status string
Status は Authorization Transaction の状態。
pending ──Complete──▶ completed ──Resume──▶ consumed │ │ └──Reject──▶ rejected ─┴──Resume──▶ consumed
func (Status) CanTransitionTo ¶
CanTransitionTo は状態機械上で許される遷移かを返す。 Adapter は遷移の原子性(CAS)を担い、遷移の正当性はこの関数で判定する。
type Store ¶
type Store interface {
// CreateTransaction は Transaction を保存する。同じ ID があれば storage.ErrAlreadyExists。
CreateTransaction(ctx context.Context, txn *Transaction) error
// GetTransaction は ID で Transaction を返す。なければ storage.ErrNotFound。
GetTransaction(ctx context.Context, id string) (*Transaction, error)
// TransitionTransaction は Status が from のときだけ to に更新し、patch を適用する(CAS)。
// Status が from でなければ storage.ErrStateConflict、存在しなければ storage.ErrNotFound。
// 遷移の正当性(from.CanTransitionTo(to))は呼び出し側が保証する。
TransitionTransaction(ctx context.Context, id string, from, to Status, patch Patch) error
// CreateAuthorizationCode は Code を保存する。同じ Hash があれば storage.ErrAlreadyExists。
CreateAuthorizationCode(ctx context.Context, code *AuthorizationCode) error
}
Store は Authorization Transaction と Authorization Code の永続化 Port。 各メソッドは単体で原子的でなければならない(ADR-0005 §1)。
type Transaction ¶
type Transaction struct {
ID string
Status Status
// Request は検証済みの認可要求。Interaction App はこれを参照してログイン・同意 UI を描く。
Request Request
// BrowserBindingHash は、/authorize でブラウザに発行した結合 Cookie 値の SHA-256。
// Resume 時に同じブラウザからの要求であることを確認する(ADR-0003 §3)。
BrowserBindingHash string
// Outcome は Complete で記録される結果。Status が completed / consumed のときのみ非 nil。
Outcome *Outcome
// Rejection は Reject で記録される結果。Status が rejected / consumed のときのみ非 nil。
Rejection *Rejection
// ResumeTokenHash は Complete / Reject で発行した単回使用トークンの SHA-256。
ResumeTokenHash string
CreatedAt time.Time
ExpiresAt time.Time
}
Transaction は 1 回の /authorize 要求から Code 発行(または失敗)までを表す。