Files
upstream-ory-kratos/selfservice/strategy/oidc/provider_uaepass_test.go
upstream-mirror c60cc7ca75
Some checks failed
CI / Run tests and lints (push) Failing after 29s
CI / Build CLI docs (push) Has been skipped
CI / Run Playwright end-to-end tests (mysql) (push) Failing after 3s
CI / Run Playwright end-to-end tests (postgres) (push) Failing after 2s
CI / Run Playwright end-to-end tests (sqlite) (push) Failing after 3s
CodeQL / Analyze (go) (push) Failing after 4s
CodeQL / Analyze (javascript) (push) Failing after 3s
Docker Image Scanners / scanners (push) Failing after 4s
Synchronize Issue Labels / Synchronize Issue Labels (push) Has been skipped
CI / Run end-to-end tests (postgres) (push) Failing after 19s
CI / Run end-to-end tests (sqlite) (push) Failing after 2s
CI / Run Playwright end-to-end tests (cockroach) (push) Failing after 2s
CI / Draft newsletter (push) Has been skipped
CI / Generate release (push) Has been skipped
CI / Pending approval Slack notification (push) Has been skipped
CI / Send newsletter (push) Has been skipped
snapshot of https://github.com/ory/kratos.git (master, 2026-09-15; blobs > 131072B omitted)
2026-09-15 21:19:12 +05:00

298 lines
9.6 KiB
Go

// Copyright © 2025 Ory Corp
// SPDX-License-Identifier: Apache-2.0
package oidc_test
import (
"net/http"
"net/http/httptest"
"net/url"
"testing"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
"golang.org/x/oauth2"
"github.com/ory/kratos/pkg"
"github.com/ory/kratos/selfservice/strategy/oidc"
)
func TestUAEPassOIDCClaims(t *testing.T) {
t.Parallel()
handler := http.NewServeMux()
expectedAccessToken := "my-access-token"
handler.HandleFunc("GET /userinfo", func(w http.ResponseWriter, r *http.Request) {
// Verify Bearer token authentication
authHeader := r.Header.Get("Authorization")
if authHeader != "Bearer "+expectedAccessToken {
w.WriteHeader(http.StatusUnauthorized)
_, _ = w.Write([]byte(`{"error": "invalid_token"}`))
return
}
// UAE PASS citizen/resident profile response
userProfile := `{
"sub": "uaepass-user-123456",
"email": "user@example.ae",
"fullnameEN": "Ahmed Mohammed Al Rashid",
"fullnameAR": "أحمد محمد الراشد",
"firstnameEN": "Ahmed",
"firstnameAR": "أحمد",
"lastnameEN": "Al Rashid",
"lastnameAR": "الراشد",
"uuid": "550e8400-e29b-41d4-a716-446655440000",
"unifiedId": "784-1990-1234567-1",
"idn": "784199012345671",
"userType": "SOP1",
"nationalityEN": "ARE",
"gender": "M",
"dob": "1990-05-15",
"mobile": "+971501234567"
}`
w.Header().Set("Content-Type", "application/json")
_, err := w.Write([]byte(userProfile))
require.NoError(t, err)
})
uaePassAPI := httptest.NewServer(handler)
t.Cleanup(uaePassAPI.Close)
_, reg := pkg.NewFastRegistryWithMocks(t)
p := oidc.NewProviderUAEPass(&oidc.Configuration{
Scope: []string{"urn:uae:digitalid:profile:general"},
}, reg).(*oidc.ProviderUAEPass)
p.SetUserInfoURL(t, uaePassAPI.URL+"/userinfo")
claims, err := p.Claims(t.Context(), &oauth2.Token{AccessToken: expectedAccessToken}, nil)
require.NoError(t, err)
require.NotNil(t, claims)
// Verify standard claims
assert.Equal(t, "uaepass-user-123456", claims.Subject)
assert.Equal(t, "user@example.ae", claims.Email)
assert.Equal(t, "Ahmed Mohammed Al Rashid", claims.Name)
assert.Equal(t, "Ahmed", claims.GivenName)
assert.Equal(t, "Al Rashid", claims.LastName)
// Verify raw claims contain UAE PASS specific data
require.NotNil(t, claims.RawClaims)
assert.Equal(t, "550e8400-e29b-41d4-a716-446655440000", claims.RawClaims["uuid"])
assert.Equal(t, "784-1990-1234567-1", claims.RawClaims["unifiedId"])
assert.Equal(t, "784199012345671", claims.RawClaims["idn"])
assert.Equal(t, "SOP1", claims.RawClaims["userType"])
assert.Equal(t, "ARE", claims.RawClaims["nationalityEN"])
assert.Equal(t, "M", claims.RawClaims["gender"])
assert.Equal(t, "1990-05-15", claims.RawClaims["dob"])
assert.Equal(t, "+971501234567", claims.RawClaims["mobile"])
assert.Equal(t, "أحمد محمد الراشد", claims.RawClaims["fullnameAR"])
}
func TestUAEPassOIDCClaimsVisitor(t *testing.T) {
t.Parallel()
handler := http.NewServeMux()
expectedAccessToken := "visitor-access-token"
handler.HandleFunc("GET /userinfo", func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader != "Bearer "+expectedAccessToken {
w.WriteHeader(http.StatusUnauthorized)
return
}
// UAE PASS visitor profile response (limited data)
userProfile := `{
"sub": "uaepass-visitor-789",
"email": "visitor@example.com",
"fullnameEN": "John Smith",
"firstnameEN": "John",
"lastnameEN": "Smith",
"uuid": "660e8400-e29b-41d4-a716-446655440001",
"userType": "SOP3"
}`
w.Header().Set("Content-Type", "application/json")
_, _ = w.Write([]byte(userProfile))
})
uaePassAPI := httptest.NewServer(handler)
t.Cleanup(uaePassAPI.Close)
_, reg := pkg.NewFastRegistryWithMocks(t)
p := oidc.NewProviderUAEPass(&oidc.Configuration{
Scope: []string{"urn:uae:digitalid:profile"},
}, reg).(*oidc.ProviderUAEPass)
p.SetUserInfoURL(t, uaePassAPI.URL+"/userinfo")
claims, err := p.Claims(t.Context(), &oauth2.Token{AccessToken: expectedAccessToken}, nil)
require.NoError(t, err)
require.NotNil(t, claims)
assert.Equal(t, "uaepass-visitor-789", claims.Subject)
assert.Equal(t, "visitor@example.com", claims.Email)
assert.Equal(t, "John Smith", claims.Name)
assert.Equal(t, "SOP3", claims.RawClaims["userType"])
}
func TestUAEPassOIDCClaimsError(t *testing.T) {
t.Parallel()
handler := http.NewServeMux()
handler.HandleFunc("GET /userinfo", func(w http.ResponseWriter, r *http.Request) {
w.WriteHeader(http.StatusUnauthorized)
_, _ = w.Write([]byte(`{"error": "invalid_token", "error_description": "The access token is expired"}`))
})
uaePassAPI := httptest.NewServer(handler)
t.Cleanup(uaePassAPI.Close)
_, reg := pkg.NewFastRegistryWithMocks(t)
p := oidc.NewProviderUAEPass(&oidc.Configuration{
Scope: []string{"urn:uae:digitalid:profile:general"},
}, reg).(*oidc.ProviderUAEPass)
p.SetUserInfoURL(t, uaePassAPI.URL+"/userinfo")
claims, err := p.Claims(t.Context(), &oauth2.Token{AccessToken: "invalid-token"}, nil)
require.Error(t, err)
require.Nil(t, claims)
}
func TestUAEPassIssuerURL(t *testing.T) {
t.Parallel()
_, reg := pkg.NewFastRegistryWithMocks(t)
// Test staging via IssuerURL
pStaging := oidc.NewProviderUAEPass(&oidc.Configuration{
IssuerURL: "https://stg-id.uaepass.ae",
Scope: []string{"urn:uae:digitalid:profile:general"},
}, reg).(*oidc.ProviderUAEPass)
oauth2Config, err := pStaging.OAuth2(t.Context())
require.NoError(t, err)
assert.Equal(t, "https://stg-id.uaepass.ae/idshub/authorize", oauth2Config.Endpoint.AuthURL)
assert.Equal(t, "https://stg-id.uaepass.ae/idshub/token", oauth2Config.Endpoint.TokenURL)
// Test production via IssuerURL
pProduction := oidc.NewProviderUAEPass(&oidc.Configuration{
IssuerURL: "https://id.uaepass.ae",
Scope: []string{"urn:uae:digitalid:profile:general"},
}, reg).(*oidc.ProviderUAEPass)
oauth2Config, err = pProduction.OAuth2(t.Context())
require.NoError(t, err)
assert.Equal(t, "https://id.uaepass.ae/idshub/authorize", oauth2Config.Endpoint.AuthURL)
assert.Equal(t, "https://id.uaepass.ae/idshub/token", oauth2Config.Endpoint.TokenURL)
// Test default (production) when IssuerURL is not set
pDefault := oidc.NewProviderUAEPass(&oidc.Configuration{
Scope: []string{"urn:uae:digitalid:profile:general"},
}, reg).(*oidc.ProviderUAEPass)
oauth2Config, err = pDefault.OAuth2(t.Context())
require.NoError(t, err)
assert.Equal(t, "https://id.uaepass.ae/idshub/authorize", oauth2Config.Endpoint.AuthURL)
assert.Equal(t, "https://id.uaepass.ae/idshub/token", oauth2Config.Endpoint.TokenURL)
// Test trailing slash is handled correctly
pTrailingSlash := oidc.NewProviderUAEPass(&oidc.Configuration{
IssuerURL: "https://stg-id.uaepass.ae/",
Scope: []string{"urn:uae:digitalid:profile:general"},
}, reg).(*oidc.ProviderUAEPass)
oauth2Config, err = pTrailingSlash.OAuth2(t.Context())
require.NoError(t, err)
assert.Equal(t, "https://stg-id.uaepass.ae/idshub/authorize", oauth2Config.Endpoint.AuthURL)
assert.Equal(t, "https://stg-id.uaepass.ae/idshub/token", oauth2Config.Endpoint.TokenURL)
}
func TestUAEPassConfigurationValidation(t *testing.T) {
t.Parallel()
_, reg := pkg.NewFastRegistryWithMocks(t)
// Test PKCE auto falls back to force (no error)
pPKCEAuto := oidc.NewProviderUAEPass(&oidc.Configuration{
PKCE: "auto",
Scope: []string{"urn:uae:digitalid:profile:general"},
}, reg).(*oidc.ProviderUAEPass)
_, err := pPKCEAuto.OAuth2(t.Context())
require.NoError(t, err)
assert.Equal(t, "force", pPKCEAuto.Config().PKCE)
// Test PKCE unset also falls back to force
pPKCEUnset := oidc.NewProviderUAEPass(&oidc.Configuration{
Scope: []string{"urn:uae:digitalid:profile:general"},
}, reg).(*oidc.ProviderUAEPass)
_, err = pPKCEUnset.OAuth2(t.Context())
require.NoError(t, err)
assert.Equal(t, "force", pPKCEUnset.Config().PKCE)
// Test valid configuration
pValid := oidc.NewProviderUAEPass(&oidc.Configuration{
PKCE: "never",
Scope: []string{"urn:uae:digitalid:profile:general"},
}, reg).(*oidc.ProviderUAEPass)
oauth2Config, err := pValid.OAuth2(t.Context())
require.NoError(t, err)
require.NotNil(t, oauth2Config)
}
func TestUAEPassAuthFlowDefaultsToWeb(t *testing.T) {
t.Parallel()
_, reg := pkg.NewFastRegistryWithMocks(t)
p := oidc.NewProviderUAEPass(&oidc.Configuration{
Scope: []string{"urn:uae:digitalid:profile:general"},
}, reg).(*oidc.ProviderUAEPass)
options := p.AuthCodeURLOptions(nil)
require.Len(t, options, 1)
oauth2Config, err := p.OAuth2(t.Context())
require.NoError(t, err)
authURL := oauth2Config.AuthCodeURL("test-state", options...)
parsedURL, err := url.Parse(authURL)
require.NoError(t, err)
acrValue := parsedURL.Query().Get("acr_values")
assert.Equal(t, oidc.UAEPassACRWeb, acrValue)
}
func TestUAEPassMobileACRViaUpstreamParams(t *testing.T) {
t.Parallel()
_, reg := pkg.NewFastRegistryWithMocks(t)
p := oidc.NewProviderUAEPass(&oidc.Configuration{
Scope: []string{"urn:uae:digitalid:profile:general"},
}, reg).(*oidc.ProviderUAEPass)
oauth2Config, err := p.OAuth2(t.Context())
require.NoError(t, err)
// Simulate the ordering in getAuthRedirectURL: provider defaults first, then upstream params.
// Upstream acr_values should override the provider's default web ACR value.
var opts []oauth2.AuthCodeOption
opts = append(opts, p.AuthCodeURLOptions(nil)...)
opts = append(opts, oidc.UpstreamParameters(map[string]string{
"acr_values": "urn:digitalid:authentication:flow:mobileondevice",
})...)
authURL := oauth2Config.AuthCodeURL("test-state", opts...)
parsedURL, err := url.Parse(authURL)
require.NoError(t, err)
acrValue := parsedURL.Query().Get("acr_values")
assert.Equal(t, "urn:digitalid:authentication:flow:mobileondevice", acrValue)
}