Server mock locale

keenauth mock serve avvia sulla tua macchina un piccolo server OpenID Connect, SAML 2.0 e SCIM 2.0, e una versione in memoria dell'API di gestione /v1, per i job CI senza accesso di rete e per lo sviluppo locale. Non richiede un ambiente KeenAuth, una chiave API né Internet. Tutti i dati risiedono in memoria e vengono eliminati quando il processo termina.

Il server mock non è KeenAuth. Implementa solo i flussi elencati qui sotto, approva automaticamente ogni richiesta e verifica molto meno del servizio reale. Il tuo ambiente development ospitato è l'unico riferimento per il comportamento reale: esegui i test end-to-end contro di esso prima di rilasciare.

Avviare il server

keenauth mock serve

Per impostazione predefinita il server ascolta su 127.0.0.1:4010, un indirizzo loopback che altre macchine non possono raggiungere. --port cambia la porta (0 ne sceglie una libera). --host cambia l'indirizzo; qualsiasi indirizzo diverso da un loopback espone il server mock alla tua rete, e la CLI stampa un avviso. Arresta il server con Ctrl+C.

Il comando non legge credenziali e non invia nulla a KeenAuth.

Endpoint

Percorso Scopo
/.well-known/openid-configuration Documento di discovery OIDC (servito anche su /.well-known/oauth-authorization-server)
/.well-known/jwks.json Set di chiavi pubbliche di test (ID chiave keenauth-test-1)
/oauth2/authorize Endpoint di autorizzazione: flusso con authorization code e PKCE (S256)
/oauth2/token Endpoint dei token: grant authorization_code e refresh_token
/oauth2/userinfo Endpoint UserInfo (access token Bearer con scope openid)
/saml-idp/<application_id>/metadata Metadati dell'identity provider SAML 2.0
/saml-idp/<application_id>/sso Endpoint SAML single sign-on (binding HTTP-Redirect e HTTP-POST)
/scim/v2/<directory_id>/… Endpoint SCIM 2.0: Users, Groups, ServiceProviderConfig, ResourceTypes, Schemas
/v1/… API di gestione in memoria: ogni operazione del documento OpenAPI dell'API di gestione

Gli URL degli endpoint nel documento di discovery e nei metadati SAML puntano all'indirizzo locale su cui ascolta il server. <application_id> e <directory_id> sono nomi a tua scelta (da 1 a 64 caratteri di A-Z, a-z, 0-9, -, _); non occorre registrare nulla in anticipo.

Come si comporta

Token e il tuo verificatore

I token sono firmati con la chiave pubblica di test del kit di test offline e portano l'issuer https://test.keenauth.invalid, non l'indirizzo locale. Il dominio di primo livello .invalid è riservato, quindi un verificatore di produzione non può mai accettare questi token: i verificatori degli SDK li accettano solo in modalità test, che attivi esplicitamente nel codice di test.

// Go: accept tokens from the mock server in a test.
v, err := keenauthtest.NewVerifier(verify.Config{Audience: "https://test.keenauth.invalid/oauth2/userinfo"})
if err != nil {
	t.Fatal(err)
}
claims, err := v.VerifyAccessToken(ctx, accessToken, nil)

Usa la stessa modalità di test in TypeScript (createTestVerifier) e in Python (keenauth.testing.verifier). Le librerie generiche per client OIDC che pretendono che l'issuer del documento di discovery coincida con l'URL da cui è stato recuperato rifiuteranno il documento del server mock; configurale con gli URL degli endpoint direttamente, oppure testale contro il tuo ambiente development.

Esempio: il flusso completo con curl

L'esempio richiede curl, openssl e jq. Avvia prima il server in un altro terminale.

BASE=http://127.0.0.1:4010
REDIRECT=http://127.0.0.1:8080/callback
VERIFIER=$(openssl rand -hex 32)
CHALLENGE=$(printf '%s' "$VERIFIER" | openssl dgst -sha256 -binary | openssl base64 -A | tr '+/' '-_' | tr -d '=')

# 1. Authorize: the server answers with a redirect that carries the code.
LOCATION=$(curl -s -o /dev/null -w '%{redirect_url}' --get "$BASE/oauth2/authorize" \
  --data-urlencode response_type=code --data-urlencode client_id=app_local \
  --data-urlencode redirect_uri="$REDIRECT" --data-urlencode 'scope=openid email offline_access' \
  --data-urlencode state=xyz --data-urlencode code_challenge="$CHALLENGE" \
  --data-urlencode code_challenge_method=S256)
CODE=$(printf '%s' "$LOCATION" | sed -E 's/.*[?&]code=([^&]+).*/\1/')

# 2. Exchange the code for tokens.
TOKENS=$(curl -s "$BASE/oauth2/token" -d grant_type=authorization_code -d client_id=app_local \
  --data-urlencode code="$CODE" --data-urlencode redirect_uri="$REDIRECT" -d code_verifier="$VERIFIER")

# 3. Call UserInfo with the access token.
curl -s "$BASE/oauth2/userinfo" -H "Authorization: Bearer $(printf '%s' "$TOKENS" | jq -r .access_token)"

# 4. Refresh.
curl -s "$BASE/oauth2/token" -d grant_type=refresh_token -d client_id=app_local \
  --data-urlencode refresh_token="$(printf '%s' "$TOKENS" | jq -r .refresh_token)"

Il passo 3 stampa l'utente di test:

{"email":"test.user@example.test","email_verified":true,"sub":"usr_01m3vnbwg0e8mbe7h7xxp6rmkc"}

Identity provider SAML

Usa il server mock come identity provider di un service provider SAML che stai sviluppando: punta il service provider all'URL dei metadati, accedi e il server mock risponderà con una risposta firmata per l'utente di test. I percorsi seguono l'identity provider SAML ospitato che KeenAuth prevede per una release successiva.

La chiave di firma è pubblica. Deriva da un'etichetta pubblicata, quindi chiunque può firmare risposte che questo certificato verifica. La sua impronta SHA-256 è fcc2fc37b9ddb800721283156f9eb26ff1b80f099d996a65650f8cb469a9be81 e il suo subject contiene TEST ONLY. Registrala solo nelle configurazioni di sviluppo e test del tuo service provider, mai in produzione. KeenAuth lo applica alle proprie connessioni SAML: negli ambienti di staging e produzione rifiuta questa chiave e qualsiasi entity ID sotto .invalid, quando importi metadati o aggiungi un certificato, e di nuovo all'accesso; gli ambienti di sviluppo le accettano.

Esempio: un accesso SAML con curl

L'esempio richiede curl e openssl. Avvia prima il server in un altro terminale.

BASE=http://127.0.0.1:4010
APP=app_local
ACS=http://127.0.0.1:8080/saml/acs

# 1. IdP metadata: entity ID, signing certificate and SSO URL for your service provider.
curl -s "$BASE/saml-idp/$APP/metadata" -o idp-metadata.xml

# 2. Send an AuthnRequest with the HTTP-POST binding (base64, no compression).
NOW=$(date -u +%Y-%m-%dT%H:%M:%SZ)
REQUEST=$(printf '<samlp:AuthnRequest xmlns:samlp="urn:oasis:names:tc:SAML:2.0:protocol" xmlns:saml="urn:oasis:names:tc:SAML:2.0:assertion" ID="_req1" Version="2.0" IssueInstant="%s" AssertionConsumerServiceURL="%s"><saml:Issuer>https://sp.example.com</saml:Issuer></samlp:AuthnRequest>' "$NOW" "$ACS" | openssl base64 -A)
curl -s "$BASE/saml-idp/$APP/sso" --data-urlencode SAMLRequest="$REQUEST" --data-urlencode RelayState=xyz -o form.html

# 3. The page posts SAMLResponse to the ACS URL by itself; decode it to look at the signed response.
sed -n 's/.*name="SAMLResponse" value="\([^"]*\)".*/\1/p' form.html | openssl base64 -d -A > response.xml
grep -o 'InResponseTo="[^"]*"' response.xml | head -1

L'ultimo comando stampa InResponseTo="_req1". Il tuo service provider convalida response.xml con il certificato in idp-metadata.xml.

Endpoint SCIM 2.0

Usa gli endpoint SCIM per testare un client SCIM, come un connettore di provisioning o uno script che crea utenti e gruppi, senza rete. Ogni <directory_id> è una directory separata e vuota che vive finché vive il processo.

Esempio: effettuare il provisioning di un utente con curl

L'esempio richiede curl e jq. Avvia prima il server in un altro terminale.

SCIM=http://127.0.0.1:4010/scim/v2/dir_local
AUTH='Authorization: Bearer keenauth-mock-scim-token'
CT='Content-Type: application/scim+json'

# 1. Create a user; the response carries the id, meta.version and an ETag header.
USER_ID=$(curl -s "$SCIM/Users" -H "$AUTH" -H "$CT" -d '{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:User"],
  "userName": "ada@example.test",
  "name": {"givenName": "Ada", "familyName": "Lovelace"},
  "emails": [{"value": "ada@example.test", "type": "work", "primary": true}]
}' | jq -r .id)

# 2. Find it with a filter.
curl -s -G "$SCIM/Users" -H "$AUTH" --data-urlencode 'filter=userName eq "ada@example.test"' | jq .totalResults

# 3. Deactivate it with PATCH, guarded by the version you read.
curl -s -X PATCH "$SCIM/Users/$USER_ID" -H "$AUTH" -H "$CT" -H 'If-Match: W/"1"' -d '{
  "schemas": ["urn:ietf:params:scim:api:messages:2.0:PatchOp"],
  "Operations": [{"op": "replace", "path": "active", "value": false}]
}' | jq -c '{active, version: .meta.version}'

# 4. Create a group with the user as a member, then delete the user.
curl -s "$SCIM/Groups" -H "$AUTH" -H "$CT" -d '{
  "schemas": ["urn:ietf:params:scim:schemas:core:2.0:Group"],
  "displayName": "Engineering",
  "members": [{"value": "'"$USER_ID"'"}]
}' | jq -c '.members[0] | {display, type}'
curl -s -o /dev/null -w '%{http_code}\n' -X DELETE "$SCIM/Users/$USER_ID" -H "$AUTH"

I comandi stampano 1, {"active":false,"version":"W/\"2\""}, {"display":"ada@example.test","type":"User"} e 204.

API di gestione (/v1)

Usa gli endpoint dell'API di gestione per testare in CI, senza rete, il codice che chiama l'API di gestione di KeenAuth (un SDK, la CLI, uno script di deployment). I percorsi sono quelli ospitati: usa l'URL base http://127.0.0.1:4010/v1 al posto di quello ospitato e nient'altro cambia. Gli endpoint sono generati dal documento OpenAPI dell'API di gestione incluso nella CLI, quindi risponde ogni operazione di quella versione dell'API.

Cosa cambia rispetto all'ambiente development. Il server mock verifica solo che il corpo sia un oggetto JSON con i campi obbligatori; non controlla formati dei campi, lunghezze, enumerazioni né unicità, e un campo di tipo errato viene ignorato. Ignora If-Match, Idempotency-Key e gli altri header e non restituisce ETag. Non controlla permessi, limiti di piano, quote, rate limit né la regione, non maschera i dati personali, non invia email, non esegue job (un job accettato resta com'era stato creato) e ha un solo ambiente. I record esistono solo per la durata del processo, al massimo 10.000. Usa il tuo ambiente development per scoprire come si comporta l'API reale.

Esempio: creare, leggere, modificare ed eliminare con curl

L'esempio richiede curl e jq. Avvia prima il server in un altro terminale.

API=http://127.0.0.1:4010/v1
AUTH='Authorization: Bearer keenauth-mock-management-token'
CT='Content-Type: application/json'

# 1. Create an organization.
ORG_ID=$(curl -s "$API/organizations" -H "$AUTH" -H "$CT" -d '{"name": "Acme"}' | jq -r .id)

# 2. Read it back and list.
curl -s "$API/organizations/$ORG_ID" -H "$AUTH" | jq -c '{id: (.id | .[0:4]), name}'
curl -s "$API/organizations" -H "$AUTH" | jq -c '{count: (.data | length), list_metadata}'

# 3. Rename it with a merge patch.
curl -s -X PATCH "$API/organizations/$ORG_ID" -H "$AUTH" -H 'Content-Type: application/merge-patch+json' -d '{"name": "Acme Inc"}' | jq -r .name

# 4. Delete it; a second read is a 404 problem.
curl -s -o /dev/null -w '%{http_code}\n' -X DELETE "$API/organizations/$ORG_ID" -H "$AUTH"
curl -s "$API/organizations/$ORG_ID" -H "$AUTH" | jq -c '{status, code}'

# 5. Without the token.
curl -s "$API/organizations" | jq -c '{status, code}'

I comandi stampano {"id":"org_","name":"Acme"}, {"count":1,"list_metadata":{"after":null,"before":null}}, Acme Inc, 204, {"status":404,"code":"not_found"} e {"status":401,"code":"unauthenticated"}.

Non supportato

Non ci sono test clock, né DPoP, né PAR, né request object, né device flow, né client credentials, né token exchange, né logout o revoca, né pagine ospitate. L'identity provider SAML non ha single logout, né asserzioni cifrate, né artifact binding, né accesso avviato dall'IdP. Gli endpoint SCIM non hanno operazioni bulk, ordinamento, /Me né gruppi annidati, e il filtro non supporta or, not, parentesi né gt/ge/lt/le. Usa il tuo ambiente development per testarli.