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:
parent
d05c73dc19
commit
4097a7de8b
5 changed files with 1679 additions and 0 deletions
79
src/internal/server/oidc/session.go
Normal file
79
src/internal/server/oidc/session.go
Normal 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)
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue