feat: implement T06, T07 — authorization endpoint, token endpoint

- T06: /authorize with full PKCE validation, Authelia delegation, MFA check
- T07: /token with RS256 JWT issuance (stdlib only), PKCE verification, scope-filtered claims

50 OIDC tests pass.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-03-13 01:56:57 +01:00
parent d05c73dc19
commit 4097a7de8b
5 changed files with 1679 additions and 0 deletions

View file

@ -0,0 +1,79 @@
package oidc
import (
"crypto/rand"
"encoding/base64"
"sync"
"time"
)
// PKCESession stores the in-flight authorization state server-side.
type PKCESession struct {
Code string
ClientID string
RedirectURI string
PKCEChallenge string // S256 challenge
PKCEChallengeMethod string // always "S256"
State string
Username string // set after auth
Scopes []string
ExpiresAt time.Time
}
// SessionStore is an in-memory PKCE session store.
type SessionStore struct {
mu sync.Mutex
sessions map[string]*PKCESession // keyed by code
}
// NewSessionStore returns an initialised, empty SessionStore.
func NewSessionStore() *SessionStore {
return &SessionStore{
sessions: make(map[string]*PKCESession),
}
}
// Create stores the session and returns the generated authorization code.
func (s *SessionStore) Create(sess *PKCESession) string {
code := generateCode()
sess.Code = code
s.mu.Lock()
s.sessions[code] = sess
s.mu.Unlock()
return code
}
// Get retrieves a session by code. Returns false if not found or expired.
func (s *SessionStore) Get(code string) (*PKCESession, bool) {
s.mu.Lock()
sess, ok := s.sessions[code]
s.mu.Unlock()
if !ok {
return nil, false
}
if time.Now().After(sess.ExpiresAt) {
s.Delete(code)
return nil, false
}
return sess, true
}
// Delete removes a session by code. No-op if the code is not present.
func (s *SessionStore) Delete(code string) {
s.mu.Lock()
delete(s.sessions, code)
s.mu.Unlock()
}
// generateCode returns a cryptographically random, URL-safe string suitable
// for use as an authorization code.
func generateCode() string {
b := make([]byte, 32)
if _, err := rand.Read(b); err != nil {
panic("oidc: failed to generate random code: " + err.Error())
}
return base64.RawURLEncoding.EncodeToString(b)
}