51 lines
2 KiB
Go
51 lines
2 KiB
Go
|
|
package oidc_test
|
||
|
|
|
||
|
|
import (
|
||
|
|
"errors"
|
||
|
|
"keycape/internal/domain"
|
||
|
|
"keycape/internal/server/oidc"
|
||
|
|
"net/http"
|
||
|
|
"net/http/httptest"
|
||
|
|
"strings"
|
||
|
|
"testing"
|
||
|
|
"time"
|
||
|
|
)
|
||
|
|
|
||
|
|
func TestOptionalMFAFollowsEnrollment(t *testing.T) {
|
||
|
|
for _, tc := range []struct {
|
||
|
|
name string
|
||
|
|
enrolled bool
|
||
|
|
lookupErr error
|
||
|
|
acr []string
|
||
|
|
want int
|
||
|
|
challenge bool
|
||
|
|
}{
|
||
|
|
{name: "unenrolled despite global requirement", want: http.StatusFound},
|
||
|
|
{name: "enrolled", enrolled: true, want: http.StatusOK, challenge: true},
|
||
|
|
{name: "lookup unavailable", lookupErr: errors.New("provider unavailable"), want: http.StatusInternalServerError},
|
||
|
|
{name: "explicit step up", acr: []string{"aal2"}, lookupErr: errors.New("must not query enrollment"), want: http.StatusOK, challenge: true},
|
||
|
|
} {
|
||
|
|
t.Run(tc.name, func(t *testing.T) {
|
||
|
|
sessions := oidc.NewSessionStore()
|
||
|
|
h := &oidc.AuthorizeHandler{
|
||
|
|
ClientConfig: map[string]*domain.Client{"test-client": {ClientID: "test-client", MFAOptional: true}},
|
||
|
|
Auth: &mockAuthProvider{callbackResult: &domain.AuthResult{Username: "alice"}},
|
||
|
|
MFA: &mockMFAProvider{required: true, requiredErr: errors.New("global policy must not be queried"), enrolled: tc.enrolled, enrolledErr: tc.lookupErr},
|
||
|
|
Sessions: sessions, Emitter: &captureEmitter{},
|
||
|
|
}
|
||
|
|
h.PendingStates().Store("optional", &oidc.PendingState{ClientID: "test-client", RedirectURI: "https://app.example/callback", State: "optional", ACRValues: tc.acr, ExpiresAt: time.Now().Add(time.Minute)})
|
||
|
|
rec := httptest.NewRecorder()
|
||
|
|
h.ServeHTTPCallback(rec, httptest.NewRequest(http.MethodGet, "/authorize/callback?code=x&state=optional", nil))
|
||
|
|
if rec.Code != tc.want {
|
||
|
|
t.Fatalf("status=%d body=%s", rec.Code, rec.Body.String())
|
||
|
|
}
|
||
|
|
if strings.Contains(rec.Body.String(), "KeyCape MFA") != tc.challenge {
|
||
|
|
t.Fatal("unexpected MFA challenge state")
|
||
|
|
}
|
||
|
|
if tc.want != http.StatusFound && strings.Contains(rec.Header().Get("Location"), "code=") {
|
||
|
|
t.Fatal("issued authorization code before MFA")
|
||
|
|
}
|
||
|
|
})
|
||
|
|
}
|
||
|
|
}
|