mirror of https://github.com/ory/hydra
563 lines
20 KiB
Go
563 lines
20 KiB
Go
// Copyright © 2022 Ory Corp
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
package oauth2_test
|
|
|
|
import (
|
|
"context"
|
|
"encoding/json"
|
|
"fmt"
|
|
"io"
|
|
"net/http"
|
|
"net/http/httptest"
|
|
"net/url"
|
|
"strings"
|
|
"testing"
|
|
"time"
|
|
|
|
"github.com/go-jose/go-jose/v3"
|
|
"github.com/gofrs/uuid"
|
|
"github.com/stretchr/testify/assert"
|
|
"github.com/stretchr/testify/require"
|
|
"github.com/tidwall/gjson"
|
|
goauth2 "golang.org/x/oauth2"
|
|
"golang.org/x/oauth2/clientcredentials"
|
|
|
|
"github.com/ory/fosite/token/jwt"
|
|
hc "github.com/ory/hydra/v2/client"
|
|
"github.com/ory/hydra/v2/driver/config"
|
|
"github.com/ory/hydra/v2/flow"
|
|
"github.com/ory/hydra/v2/internal/testhelpers"
|
|
"github.com/ory/hydra/v2/jwk"
|
|
hydraoauth2 "github.com/ory/hydra/v2/oauth2"
|
|
"github.com/ory/hydra/v2/oauth2/trust"
|
|
"github.com/ory/hydra/v2/x"
|
|
"github.com/ory/x/contextx"
|
|
)
|
|
|
|
func TestJWTBearer(t *testing.T) {
|
|
t.Parallel()
|
|
|
|
ctx := context.Background()
|
|
reg := testhelpers.NewMockedRegistry(t, &contextx.Default{})
|
|
reg.Config().MustSet(ctx, config.KeyAccessTokenStrategy, "opaque")
|
|
_, admin := testhelpers.NewOAuth2Server(ctx, t, reg)
|
|
|
|
secret := uuid.Must(uuid.NewV4()).String()
|
|
client := &hc.Client{
|
|
Secret: secret,
|
|
GrantTypes: []string{"client_credentials", "urn:ietf:params:oauth:grant-type:jwt-bearer"},
|
|
Scope: "offline_access",
|
|
}
|
|
require.NoError(t, reg.ClientManager().CreateClient(ctx, client))
|
|
|
|
newConf := func(client *hc.Client) *clientcredentials.Config {
|
|
return &clientcredentials.Config{
|
|
ClientID: client.GetID(),
|
|
ClientSecret: secret,
|
|
TokenURL: reg.Config().OAuth2TokenURL(ctx).String(),
|
|
Scopes: strings.Split(client.Scope, " "),
|
|
EndpointParams: url.Values{"audience": client.Audience},
|
|
}
|
|
}
|
|
|
|
var getToken = func(t *testing.T, conf *clientcredentials.Config) (*goauth2.Token, error) {
|
|
if conf.AuthStyle == goauth2.AuthStyleAutoDetect {
|
|
conf.AuthStyle = goauth2.AuthStyleInHeader
|
|
}
|
|
return conf.Token(context.Background())
|
|
}
|
|
|
|
var inspectToken = func(t *testing.T, token *goauth2.Token, cl *hc.Client, strategy string, grant trust.Grant, checkExtraClaims bool) {
|
|
introspection := testhelpers.IntrospectToken(t, &goauth2.Config{ClientID: cl.GetID(), ClientSecret: cl.Secret}, token.AccessToken, admin)
|
|
|
|
check := func(res gjson.Result) {
|
|
assert.EqualValues(t, cl.GetID(), res.Get("client_id").String(), "%s", res.Raw)
|
|
assert.EqualValues(t, grant.Subject, res.Get("sub").String(), "%s", res.Raw)
|
|
assert.EqualValues(t, reg.Config().IssuerURL(ctx).String(), res.Get("iss").String(), "%s", res.Raw)
|
|
|
|
assert.EqualValues(t, res.Get("nbf").Int(), res.Get("iat").Int(), "%s", res.Raw)
|
|
assert.True(t, res.Get("exp").Int() >= res.Get("iat").Int()+int64(reg.Config().GetAccessTokenLifespan(ctx).Seconds()), "%s", res.Raw)
|
|
|
|
assert.EqualValues(t, fmt.Sprintf(`["%s"]`, reg.Config().OAuth2TokenURL(ctx).String()), res.Get("aud").Raw, "%s", res.Raw)
|
|
|
|
if checkExtraClaims {
|
|
require.True(t, res.Get("ext.hooked").Bool())
|
|
}
|
|
}
|
|
|
|
check(introspection)
|
|
assert.True(t, introspection.Get("active").Bool())
|
|
assert.EqualValues(t, "access_token", introspection.Get("token_use").String())
|
|
assert.EqualValues(t, "Bearer", introspection.Get("token_type").String())
|
|
assert.EqualValues(t, "offline_access", introspection.Get("scope").String(), "%s", introspection.Raw)
|
|
|
|
if strategy != "jwt" {
|
|
return
|
|
}
|
|
|
|
body, err := x.DecodeSegment(strings.Split(token.AccessToken, ".")[1])
|
|
require.NoError(t, err)
|
|
jwtClaims := gjson.ParseBytes(body)
|
|
assert.NotEmpty(t, jwtClaims.Get("jti").String())
|
|
assert.NotEmpty(t, jwtClaims.Get("iss").String())
|
|
assert.NotEmpty(t, jwtClaims.Get("client_id").String())
|
|
assert.EqualValues(t, "offline_access", introspection.Get("scope").String(), "%s", introspection.Raw)
|
|
|
|
header, err := x.DecodeSegment(strings.Split(token.AccessToken, ".")[0])
|
|
require.NoError(t, err)
|
|
jwtHeader := gjson.ParseBytes(header)
|
|
assert.NotEmpty(t, jwtHeader.Get("kid").String())
|
|
assert.EqualValues(t, "offline_access", introspection.Get("scope").String(), "%s", introspection.Raw)
|
|
|
|
check(jwtClaims)
|
|
}
|
|
|
|
t.Run("case=unable to exchange invalid jwt", func(t *testing.T) {
|
|
conf := newConf(client)
|
|
conf.EndpointParams = url.Values{"grant_type": {"urn:ietf:params:oauth:grant-type:jwt-bearer"}, "assertion": {"not-a-jwt"}}
|
|
_, err := getToken(t, conf)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "Unable to parse JSON Web Token")
|
|
})
|
|
|
|
t.Run("case=unable to request grant if not set", func(t *testing.T) {
|
|
client := &hc.Client{
|
|
Secret: secret,
|
|
GrantTypes: []string{"client_credentials"},
|
|
Scope: "offline_access",
|
|
}
|
|
require.NoError(t, reg.ClientManager().CreateClient(ctx, client))
|
|
|
|
conf := newConf(client)
|
|
conf.EndpointParams = url.Values{"grant_type": {"urn:ietf:params:oauth:grant-type:jwt-bearer"}, "assertion": {"not-a-jwt"}}
|
|
_, err := getToken(t, conf)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "urn:ietf:params:oauth:grant-type:jwt-bearer")
|
|
})
|
|
|
|
set, kid := uuid.Must(uuid.NewV4()).String(), uuid.Must(uuid.NewV4()).String()
|
|
keys, err := jwk.GenerateJWK(ctx, jose.RS256, kid, "sig")
|
|
require.NoError(t, err)
|
|
trustGrant := trust.Grant{
|
|
ID: uuid.Must(uuid.NewV4()).String(),
|
|
Issuer: set,
|
|
Subject: uuid.Must(uuid.NewV4()).String(),
|
|
AllowAnySubject: false,
|
|
Scope: []string{"offline_access"},
|
|
ExpiresAt: time.Now().Add(time.Hour),
|
|
PublicKey: trust.PublicKey{Set: set, KeyID: kid},
|
|
}
|
|
require.NoError(t, reg.GrantManager().CreateGrant(ctx, trustGrant, keys.Keys[0].Public()))
|
|
signer := jwk.NewDefaultJWTSigner(reg.Config(), reg, set)
|
|
signer.GetPrivateKey = func(ctx context.Context) (interface{}, error) {
|
|
return keys.Keys[0], nil
|
|
}
|
|
|
|
t.Run("case=unable to exchange token with a non-allowed subject", func(t *testing.T) {
|
|
token, _, err := signer.Generate(ctx, jwt.MapClaims{
|
|
"jti": uuid.Must(uuid.NewV4()).String(),
|
|
"iss": trustGrant.Issuer,
|
|
"sub": uuid.Must(uuid.NewV4()).String(),
|
|
"aud": reg.Config().OAuth2TokenURL(ctx).String(),
|
|
"exp": time.Now().Add(time.Hour).Unix(),
|
|
"iat": time.Now().Add(-time.Minute).Unix(),
|
|
}, &jwt.Headers{Extra: map[string]interface{}{"kid": kid}})
|
|
require.NoError(t, err)
|
|
|
|
conf := newConf(client)
|
|
conf.EndpointParams = url.Values{"grant_type": {"urn:ietf:params:oauth:grant-type:jwt-bearer"}, "assertion": {token}}
|
|
_, err = getToken(t, conf)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "public key is required to check signature of JWT")
|
|
})
|
|
|
|
t.Run("case=unable to exchange token with non-allowed scope", func(t *testing.T) {
|
|
token, _, err := signer.Generate(ctx, jwt.MapClaims{
|
|
"jti": uuid.Must(uuid.NewV4()).String(),
|
|
"iss": trustGrant.Issuer,
|
|
"sub": trustGrant.Subject,
|
|
"aud": reg.Config().OAuth2TokenURL(ctx).String(),
|
|
"exp": time.Now().Add(time.Hour).Unix(),
|
|
"iat": time.Now().Add(-time.Minute).Unix(),
|
|
}, &jwt.Headers{Extra: map[string]interface{}{"kid": kid}})
|
|
require.NoError(t, err)
|
|
|
|
conf := newConf(client)
|
|
conf.Scopes = []string{"i_am_not_allowed"}
|
|
conf.EndpointParams = url.Values{"grant_type": {"urn:ietf:params:oauth:grant-type:jwt-bearer"}, "assertion": {token}}
|
|
_, err = getToken(t, conf)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "i_am_not_allowed")
|
|
})
|
|
|
|
t.Run("case=unable to exchange token with an unknown kid", func(t *testing.T) {
|
|
token, _, err := signer.Generate(ctx, jwt.MapClaims{
|
|
"jti": uuid.Must(uuid.NewV4()).String(),
|
|
"iss": trustGrant.Issuer,
|
|
"sub": trustGrant.Subject,
|
|
"aud": reg.Config().OAuth2TokenURL(ctx).String(),
|
|
"exp": time.Now().Add(time.Hour).Unix(),
|
|
"iat": time.Now().Add(-time.Minute).Unix(),
|
|
}, &jwt.Headers{Extra: map[string]interface{}{"kid": uuid.Must(uuid.NewV4()).String()}})
|
|
require.NoError(t, err)
|
|
|
|
conf := newConf(client)
|
|
conf.EndpointParams = url.Values{"grant_type": {"urn:ietf:params:oauth:grant-type:jwt-bearer"}, "assertion": {token}}
|
|
_, err = getToken(t, conf)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "public key is required to check signature of JWT")
|
|
})
|
|
|
|
t.Run("case=unable to exchange token with an invalid key", func(t *testing.T) {
|
|
keys, err := jwk.GenerateJWK(ctx, jose.RS256, kid, "sig")
|
|
require.NoError(t, err)
|
|
signer := jwk.NewDefaultJWTSigner(reg.Config(), reg, set)
|
|
signer.GetPrivateKey = func(ctx context.Context) (interface{}, error) {
|
|
return keys.Keys[0], nil
|
|
}
|
|
|
|
token, _, err := signer.Generate(ctx, jwt.MapClaims{
|
|
"jti": uuid.Must(uuid.NewV4()).String(),
|
|
"iss": trustGrant.Issuer,
|
|
"sub": trustGrant.Subject,
|
|
"aud": reg.Config().OAuth2TokenURL(ctx).String(),
|
|
"exp": time.Now().Add(time.Hour).Unix(),
|
|
"iat": time.Now().Add(-time.Minute).Unix(),
|
|
}, &jwt.Headers{Extra: map[string]interface{}{"kid": kid}})
|
|
require.NoError(t, err)
|
|
|
|
conf := newConf(client)
|
|
conf.EndpointParams = url.Values{"grant_type": {"urn:ietf:params:oauth:grant-type:jwt-bearer"}, "assertion": {token}}
|
|
_, err = getToken(t, conf)
|
|
require.Error(t, err)
|
|
assert.Contains(t, err.Error(), "Unable to verify the integrity")
|
|
})
|
|
|
|
t.Run("case=should exchange for an access token", func(t *testing.T) {
|
|
run := func(strategy string) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
reg.Config().MustSet(ctx, config.KeyAccessTokenStrategy, strategy)
|
|
|
|
token, _, err := signer.Generate(ctx, jwt.MapClaims{
|
|
"jti": uuid.Must(uuid.NewV4()).String(),
|
|
"iss": trustGrant.Issuer,
|
|
"sub": trustGrant.Subject,
|
|
"aud": reg.Config().OAuth2TokenURL(ctx).String(),
|
|
"exp": time.Now().Add(time.Hour).Unix(),
|
|
"iat": time.Now().Add(-time.Minute).Unix(),
|
|
}, &jwt.Headers{Extra: map[string]interface{}{"kid": kid}})
|
|
require.NoError(t, err)
|
|
|
|
conf := newConf(client)
|
|
conf.EndpointParams = url.Values{"grant_type": {"urn:ietf:params:oauth:grant-type:jwt-bearer"}, "assertion": {token}}
|
|
|
|
result, err := getToken(t, conf)
|
|
require.NoError(t, err)
|
|
|
|
inspectToken(t, result, client, strategy, trustGrant, false)
|
|
}
|
|
}
|
|
|
|
t.Run("strategy=opaque", run("opaque"))
|
|
t.Run("strategy=jwt", run("jwt"))
|
|
})
|
|
|
|
t.Run("case=exchange for an access token without client", func(t *testing.T) {
|
|
t.Skip("This currently does not work because the client is a required foreign key and also required throughout the code base.")
|
|
|
|
run := func(strategy string) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
reg.Config().MustSet(ctx, config.KeyAccessTokenStrategy, strategy)
|
|
reg.Config().MustSet(ctx, "config.KeyOAuth2GrantJWTClientAuthOptional", true)
|
|
|
|
token, _, err := signer.Generate(ctx, jwt.MapClaims{
|
|
"jti": uuid.Must(uuid.NewV4()).String(),
|
|
"iss": trustGrant.Issuer,
|
|
"sub": trustGrant.Subject,
|
|
"aud": reg.Config().OAuth2TokenURL(ctx).String(),
|
|
"exp": time.Now().Add(time.Hour).Unix(),
|
|
"iat": time.Now().Add(-time.Minute).Unix(),
|
|
}, &jwt.Headers{Extra: map[string]interface{}{"kid": kid}})
|
|
require.NoError(t, err)
|
|
|
|
res, err := http.DefaultClient.PostForm(reg.Config().OAuth2TokenURL(ctx).String(), url.Values{
|
|
"grant_type": {"urn:ietf:params:oauth:grant-type:jwt-bearer"},
|
|
"assertion": {token},
|
|
})
|
|
require.NoError(t, err)
|
|
defer res.Body.Close()
|
|
body, err := io.ReadAll(res.Body)
|
|
require.NoError(t, err)
|
|
require.EqualValues(t, http.StatusOK, res.StatusCode, "%s", body)
|
|
|
|
var result goauth2.Token
|
|
require.NoError(t, json.Unmarshal(body, &result))
|
|
assert.NotEmpty(t, result.AccessToken, "%s", body)
|
|
|
|
inspectToken(t, &result, client, strategy, trustGrant, false)
|
|
}
|
|
}
|
|
|
|
t.Run("strategy=opaque", run("opaque"))
|
|
t.Run("strategy=jwt", run("jwt"))
|
|
})
|
|
|
|
t.Run("should call token hook if configured", func(t *testing.T) {
|
|
run := func(strategy string) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
audience := reg.Config().OAuth2TokenURL(ctx).String()
|
|
grantType := "urn:ietf:params:oauth:grant-type:jwt-bearer"
|
|
|
|
token, _, err := signer.Generate(ctx, jwt.MapClaims{
|
|
"jti": uuid.Must(uuid.NewV4()).String(),
|
|
"iss": trustGrant.Issuer,
|
|
"sub": trustGrant.Subject,
|
|
"aud": audience,
|
|
"exp": time.Now().Add(time.Hour).Unix(),
|
|
"iat": time.Now().Add(-time.Minute).Unix(),
|
|
}, &jwt.Headers{Extra: map[string]interface{}{"kid": kid}})
|
|
require.NoError(t, err)
|
|
|
|
hs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
assert.Equal(t, r.Header.Get("Content-Type"), "application/json; charset=UTF-8")
|
|
|
|
expectedGrantedScopes := []string{client.Scope}
|
|
expectedGrantedAudience := []string{audience}
|
|
expectedPayload := map[string][]string{
|
|
"assertion": {token},
|
|
"grant_type": {"urn:ietf:params:oauth:grant-type:jwt-bearer"},
|
|
"scope": {"offline_access"},
|
|
}
|
|
|
|
var hookReq hydraoauth2.TokenHookRequest
|
|
require.NoError(t, json.NewDecoder(r.Body).Decode(&hookReq))
|
|
require.NotEmpty(t, hookReq.Session)
|
|
require.Equal(t, hookReq.Session.Extra, map[string]interface{}{})
|
|
require.NotEmpty(t, hookReq.Request)
|
|
require.ElementsMatch(t, hookReq.Request.GrantedScopes, expectedGrantedScopes)
|
|
require.ElementsMatch(t, hookReq.Request.GrantedAudience, expectedGrantedAudience)
|
|
require.Equal(t, expectedPayload, hookReq.Request.Payload)
|
|
|
|
claims := map[string]interface{}{
|
|
"hooked": true,
|
|
}
|
|
|
|
hookResp := hydraoauth2.TokenHookResponse{
|
|
Session: flow.AcceptOAuth2ConsentRequestSession{
|
|
AccessToken: claims,
|
|
IDToken: claims,
|
|
},
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
require.NoError(t, json.NewEncoder(w).Encode(&hookResp))
|
|
}))
|
|
defer hs.Close()
|
|
|
|
reg.Config().MustSet(ctx, config.KeyAccessTokenStrategy, strategy)
|
|
reg.Config().MustSet(ctx, config.KeyTokenHook, hs.URL)
|
|
|
|
defer reg.Config().MustSet(ctx, config.KeyTokenHook, nil)
|
|
|
|
conf := newConf(client)
|
|
conf.EndpointParams = url.Values{"grant_type": {grantType}, "assertion": {token}}
|
|
|
|
result, err := getToken(t, conf)
|
|
require.NoError(t, err)
|
|
|
|
inspectToken(t, result, client, strategy, trustGrant, true)
|
|
}
|
|
}
|
|
|
|
t.Run("strategy=opaque", run("opaque"))
|
|
t.Run("strategy=jwt", run("jwt"))
|
|
})
|
|
|
|
t.Run("should call token hook if configured and omit client_secret from payload", func(t *testing.T) {
|
|
run := func(strategy string) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
audience := reg.Config().OAuth2TokenURL(ctx).String()
|
|
grantType := "urn:ietf:params:oauth:grant-type:jwt-bearer"
|
|
|
|
token, _, err := signer.Generate(ctx, jwt.MapClaims{
|
|
"jti": uuid.Must(uuid.NewV4()).String(),
|
|
"iss": trustGrant.Issuer,
|
|
"sub": trustGrant.Subject,
|
|
"aud": audience,
|
|
"exp": time.Now().Add(time.Hour).Unix(),
|
|
"iat": time.Now().Add(-time.Minute).Unix(),
|
|
}, &jwt.Headers{Extra: map[string]interface{}{"kid": kid}})
|
|
require.NoError(t, err)
|
|
|
|
client := &hc.Client{
|
|
Secret: secret,
|
|
GrantTypes: []string{"urn:ietf:params:oauth:grant-type:jwt-bearer"},
|
|
Scope: "offline_access",
|
|
TokenEndpointAuthMethod: "client_secret_post",
|
|
}
|
|
require.NoError(t, reg.ClientManager().CreateClient(ctx, client))
|
|
|
|
hs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
assert.Equal(t, r.Header.Get("Content-Type"), "application/json; charset=UTF-8")
|
|
|
|
expectedGrantedScopes := []string{client.Scope}
|
|
expectedGrantedAudience := []string{audience}
|
|
expectedPayload := map[string][]string{
|
|
"assertion": {token},
|
|
"client_id": {client.GetID()},
|
|
"grant_type": {"urn:ietf:params:oauth:grant-type:jwt-bearer"},
|
|
"scope": {"offline_access"},
|
|
}
|
|
|
|
var hookReq hydraoauth2.TokenHookRequest
|
|
require.NoError(t, json.NewDecoder(r.Body).Decode(&hookReq))
|
|
require.NotEmpty(t, hookReq.Session)
|
|
require.Equal(t, hookReq.Session.Extra, map[string]interface{}{})
|
|
require.NotEmpty(t, hookReq.Request)
|
|
require.ElementsMatch(t, hookReq.Request.GrantedScopes, expectedGrantedScopes)
|
|
require.ElementsMatch(t, hookReq.Request.GrantedAudience, expectedGrantedAudience)
|
|
require.Equal(t, hookReq.Request.Payload, expectedPayload)
|
|
|
|
claims := map[string]interface{}{
|
|
"hooked": true,
|
|
}
|
|
|
|
hookResp := hydraoauth2.TokenHookResponse{
|
|
Session: flow.AcceptOAuth2ConsentRequestSession{
|
|
AccessToken: claims,
|
|
IDToken: claims,
|
|
},
|
|
}
|
|
|
|
w.WriteHeader(http.StatusOK)
|
|
require.NoError(t, json.NewEncoder(w).Encode(&hookResp))
|
|
}))
|
|
defer hs.Close()
|
|
|
|
reg.Config().MustSet(ctx, config.KeyAccessTokenStrategy, strategy)
|
|
reg.Config().MustSet(ctx, config.KeyTokenHook, hs.URL)
|
|
|
|
defer reg.Config().MustSet(ctx, config.KeyTokenHook, nil)
|
|
|
|
conf := newConf(client)
|
|
conf.AuthStyle = goauth2.AuthStyleInParams
|
|
conf.EndpointParams = url.Values{"grant_type": {grantType}, "assertion": {token}}
|
|
|
|
result, err := getToken(t, conf)
|
|
require.NoError(t, err)
|
|
|
|
inspectToken(t, result, client, strategy, trustGrant, true)
|
|
}
|
|
}
|
|
|
|
t.Run("strategy=opaque", run("opaque"))
|
|
t.Run("strategy=jwt", run("jwt"))
|
|
})
|
|
|
|
t.Run("should fail token if hook fails", func(t *testing.T) {
|
|
run := func(strategy string) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
hs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusInternalServerError)
|
|
}))
|
|
defer hs.Close()
|
|
|
|
reg.Config().MustSet(ctx, config.KeyAccessTokenStrategy, strategy)
|
|
reg.Config().MustSet(ctx, config.KeyTokenHook, hs.URL)
|
|
|
|
defer reg.Config().MustSet(ctx, config.KeyTokenHook, nil)
|
|
|
|
token, _, err := signer.Generate(ctx, jwt.MapClaims{
|
|
"jti": uuid.Must(uuid.NewV4()).String(),
|
|
"iss": trustGrant.Issuer,
|
|
"sub": trustGrant.Subject,
|
|
"aud": reg.Config().OAuth2TokenURL(ctx).String(),
|
|
"exp": time.Now().Add(time.Hour).Unix(),
|
|
"iat": time.Now().Add(-time.Minute).Unix(),
|
|
}, &jwt.Headers{Extra: map[string]interface{}{"kid": kid}})
|
|
require.NoError(t, err)
|
|
|
|
conf := newConf(client)
|
|
conf.EndpointParams = url.Values{"grant_type": {"urn:ietf:params:oauth:grant-type:jwt-bearer"}, "assertion": {token}}
|
|
|
|
_, tokenError := getToken(t, conf)
|
|
require.Error(t, tokenError)
|
|
}
|
|
}
|
|
|
|
t.Run("strategy=opaque", run("opaque"))
|
|
t.Run("strategy=jwt", run("jwt"))
|
|
})
|
|
|
|
t.Run("should fail token if hook denied the request", func(t *testing.T) {
|
|
run := func(strategy string) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
hs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusForbidden)
|
|
}))
|
|
defer hs.Close()
|
|
|
|
reg.Config().MustSet(ctx, config.KeyAccessTokenStrategy, strategy)
|
|
reg.Config().MustSet(ctx, config.KeyTokenHook, hs.URL)
|
|
|
|
defer reg.Config().MustSet(ctx, config.KeyTokenHook, nil)
|
|
|
|
token, _, err := signer.Generate(ctx, jwt.MapClaims{
|
|
"jti": uuid.Must(uuid.NewV4()).String(),
|
|
"iss": trustGrant.Issuer,
|
|
"sub": trustGrant.Subject,
|
|
"aud": reg.Config().OAuth2TokenURL(ctx).String(),
|
|
"exp": time.Now().Add(time.Hour).Unix(),
|
|
"iat": time.Now().Add(-time.Minute).Unix(),
|
|
}, &jwt.Headers{Extra: map[string]interface{}{"kid": kid}})
|
|
require.NoError(t, err)
|
|
|
|
conf := newConf(client)
|
|
conf.EndpointParams = url.Values{"grant_type": {"urn:ietf:params:oauth:grant-type:jwt-bearer"}, "assertion": {token}}
|
|
|
|
_, tokenError := getToken(t, conf)
|
|
require.Error(t, tokenError)
|
|
}
|
|
}
|
|
|
|
t.Run("strategy=opaque", run("opaque"))
|
|
t.Run("strategy=jwt", run("jwt"))
|
|
})
|
|
|
|
t.Run("should fail token if hook response is malformed", func(t *testing.T) {
|
|
run := func(strategy string) func(t *testing.T) {
|
|
return func(t *testing.T) {
|
|
hs := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
|
w.WriteHeader(http.StatusOK)
|
|
}))
|
|
defer hs.Close()
|
|
|
|
reg.Config().MustSet(ctx, config.KeyAccessTokenStrategy, strategy)
|
|
reg.Config().MustSet(ctx, config.KeyTokenHook, hs.URL)
|
|
|
|
defer reg.Config().MustSet(ctx, config.KeyTokenHook, nil)
|
|
|
|
token, _, err := signer.Generate(ctx, jwt.MapClaims{
|
|
"jti": uuid.Must(uuid.NewV4()).String(),
|
|
"iss": trustGrant.Issuer,
|
|
"sub": trustGrant.Subject,
|
|
"aud": reg.Config().OAuth2TokenURL(ctx).String(),
|
|
"exp": time.Now().Add(time.Hour).Unix(),
|
|
"iat": time.Now().Add(-time.Minute).Unix(),
|
|
}, &jwt.Headers{Extra: map[string]interface{}{"kid": kid}})
|
|
require.NoError(t, err)
|
|
|
|
conf := newConf(client)
|
|
conf.EndpointParams = url.Values{"grant_type": {"urn:ietf:params:oauth:grant-type:jwt-bearer"}, "assertion": {token}}
|
|
|
|
_, tokenError := getToken(t, conf)
|
|
require.Error(t, tokenError)
|
|
}
|
|
}
|
|
|
|
t.Run("strategy=opaque", run("opaque"))
|
|
t.Run("strategy=jwt", run("jwt"))
|
|
})
|
|
}
|