Implement KeyCape provider and service identity contracts
All checks were successful
Build and Publish Container Image / build-and-push (push) Successful in 25s

Assistant: codex
Assistant-Model: gpt-5.6-sol
Assistant-Session: 01a02e3f-7301-7622-9be1-12e5f352881c
This commit is contained in:
tegwick 2026-08-23 13:10:13 +02:00
parent cdfb046b80
commit efce3e9331
15 changed files with 579 additions and 26 deletions

View file

@ -232,11 +232,15 @@ func (h *TokenHandler) serveClientCredentials(w http.ResponseWriter, r *http.Req
}
now := time.Now()
tokenLifetime := h.TokenLifetime
if client.TokenLifetime > 0 {
tokenLifetime = client.TokenLifetime
}
claims := map[string]interface{}{
"iss": h.Issuer,
"sub": client.ServiceSubject,
"aud": clientID,
"exp": now.Add(h.TokenLifetime).Unix(),
"exp": now.Add(tokenLifetime).Unix(),
"iat": now.Unix(),
"tenant": client.Tenant,
"principal_type": "service",
@ -265,7 +269,7 @@ func (h *TokenHandler) serveClientCredentials(w http.ResponseWriter, r *http.Req
w.WriteHeader(http.StatusOK)
_ = json.NewEncoder(w).Encode(tokenResponse{
AccessToken: jwtToken, TokenType: "Bearer",
ExpiresIn: int(h.TokenLifetime.Seconds()),
ExpiresIn: int(tokenLifetime.Seconds()),
})
}

View file

@ -291,6 +291,30 @@ func TestTokenHandler_ClientCredentials_ReturnsScopedServiceToken(t *testing.T)
}
}
func TestTokenHandler_ClientCredentials_UsesPerClientLifetime(t *testing.T) {
h := serviceTokenHandler(t)
h.ClientConfig["rapp-qonto"].TokenLifetime = 5 * time.Minute
req := tokenRequest(url.Values{
"grant_type": {"client_credentials"},
"scope": {"finance.qonto.read"},
})
req.SetBasicAuth("rapp-qonto", "test-service-secret")
w := httptest.NewRecorder()
h.ServeHTTP(w, req)
if w.Code != http.StatusOK {
t.Fatalf("expected 200, got %d: %s", w.Code, w.Body.String())
}
resp := decodeTokenResponse(t, w.Body.String())
if got := int(resp["expires_in"].(float64)); got != 300 {
t.Fatalf("expires_in: want 300, got %d", got)
}
claims := parseJWTPayload(t, resp["access_token"].(string))
ttl := int64(claims["exp"].(float64) - claims["iat"].(float64))
if ttl != 300 {
t.Fatalf("JWT lifetime: want 300 seconds, got %d", ttl)
}
}
func TestTokenHandler_ClientCredentials_RejectsWrongSecret(t *testing.T) {
h := serviceTokenHandler(t)
req := tokenRequest(url.Values{"grant_type": {"client_credentials"}})