mirror of
https://github.com/coder/coder.git
synced 2025-07-03 16:13:58 +00:00
feat: add sourcing secondary claims from access_token (#16517)
Niche edge case, assumes access_token is jwt. Some `access_token`s are JWT's with potential useful claims. These claims would be nearly equivalent to `user_info` claims. This is not apart of the oauth spec, so this feature should not be loudly advertised. If using this feature, alternate solutions are preferred.
This commit is contained in:
@ -105,6 +105,7 @@ type FakeIDP struct {
|
||||
// "Authorized Redirect URLs". This can be used to emulate that.
|
||||
hookValidRedirectURL func(redirectURL string) error
|
||||
hookUserInfo func(email string) (jwt.MapClaims, error)
|
||||
hookAccessTokenJWT func(email string, exp time.Time) jwt.MapClaims
|
||||
// defaultIDClaims is if a new client connects and we didn't preset
|
||||
// some claims.
|
||||
defaultIDClaims jwt.MapClaims
|
||||
@ -154,6 +155,12 @@ func WithMiddlewares(mws ...func(http.Handler) http.Handler) func(*FakeIDP) {
|
||||
}
|
||||
}
|
||||
|
||||
func WithAccessTokenJWTHook(hook func(email string, exp time.Time) jwt.MapClaims) func(*FakeIDP) {
|
||||
return func(f *FakeIDP) {
|
||||
f.hookAccessTokenJWT = hook
|
||||
}
|
||||
}
|
||||
|
||||
func WithHookWellKnown(hook func(r *http.Request, j *ProviderJSON) error) func(*FakeIDP) {
|
||||
return func(f *FakeIDP) {
|
||||
f.hookWellKnown = hook
|
||||
@ -316,8 +323,7 @@ const (
|
||||
func NewFakeIDP(t testing.TB, opts ...FakeIDPOpt) *FakeIDP {
|
||||
t.Helper()
|
||||
|
||||
block, _ := pem.Decode([]byte(testRSAPrivateKey))
|
||||
pkey, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
pkey, err := FakeIDPKey()
|
||||
require.NoError(t, err)
|
||||
|
||||
idp := &FakeIDP{
|
||||
@ -676,8 +682,13 @@ func (f *FakeIDP) newCode(state string) string {
|
||||
|
||||
// newToken enforces the access token exchanged is actually a valid access token
|
||||
// created by the IDP.
|
||||
func (f *FakeIDP) newToken(email string, expires time.Time) string {
|
||||
func (f *FakeIDP) newToken(t testing.TB, email string, expires time.Time) string {
|
||||
accessToken := uuid.NewString()
|
||||
if f.hookAccessTokenJWT != nil {
|
||||
claims := f.hookAccessTokenJWT(email, expires)
|
||||
accessToken = f.encodeClaims(t, claims)
|
||||
}
|
||||
|
||||
f.accessTokens.Store(accessToken, token{
|
||||
issued: time.Now(),
|
||||
email: email,
|
||||
@ -963,7 +974,7 @@ func (f *FakeIDP) httpHandler(t testing.TB) http.Handler {
|
||||
email := getEmail(claims)
|
||||
refreshToken := f.newRefreshTokens(email)
|
||||
token := map[string]interface{}{
|
||||
"access_token": f.newToken(email, exp),
|
||||
"access_token": f.newToken(t, email, exp),
|
||||
"refresh_token": refreshToken,
|
||||
"token_type": "Bearer",
|
||||
"expires_in": int64((f.defaultExpire).Seconds()),
|
||||
@ -1465,9 +1476,10 @@ func (f *FakeIDP) internalOIDCConfig(ctx context.Context, t testing.TB, scopes [
|
||||
Verifier: oidc.NewVerifier(f.provider.Issuer, &oidc.StaticKeySet{
|
||||
PublicKeys: []crypto.PublicKey{f.key.Public()},
|
||||
}, verifierConfig),
|
||||
UsernameField: "preferred_username",
|
||||
EmailField: "email",
|
||||
AuthURLParams: map[string]string{"access_type": "offline"},
|
||||
UsernameField: "preferred_username",
|
||||
EmailField: "email",
|
||||
AuthURLParams: map[string]string{"access_type": "offline"},
|
||||
SecondaryClaims: coderd.MergedClaimsSourceUserInfo,
|
||||
}
|
||||
|
||||
for _, opt := range opts {
|
||||
@ -1552,3 +1564,8 @@ d8h4Ht09E+f3nhTEc87mODkl7WJZpHL6V2sORfeq/eIkds+H6CJ4hy5w/bSw8tjf
|
||||
sz9Di8sGIaUbLZI2rd0CQQCzlVwEtRtoNCyMJTTrkgUuNufLP19RZ5FpyXxBO5/u
|
||||
QastnN77KfUwdj3SJt44U/uh1jAIv4oSLBr8HYUkbnI8
|
||||
-----END RSA PRIVATE KEY-----`
|
||||
|
||||
func FakeIDPKey() (*rsa.PrivateKey, error) {
|
||||
block, _ := pem.Decode([]byte(testRSAPrivateKey))
|
||||
return x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
}
|
||||
|
Reference in New Issue
Block a user