mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-07-16 04:03:47 +03:00
60 lines
2.6 KiB
Go
60 lines
2.6 KiB
Go
package oidc
|
|
|
|
import (
|
|
"context"
|
|
"slices"
|
|
|
|
"github.com/ory/fosite"
|
|
fositeoauth2 "github.com/ory/fosite/handler/oauth2"
|
|
)
|
|
|
|
// isIdentityScope reports whether the scope is an OIDC identity scope whose presence lets a token be presented to Pocket ID's own identity endpoints such as /userinfo
|
|
// offline_access is deliberately excluded: it only requests a refresh token and is not tied to any resource server
|
|
func isIdentityScope(scope string) bool {
|
|
return scope != "offline_access" && isStandardScope(scope)
|
|
}
|
|
|
|
// hasIdentityScope reports whether any of the granted scopes is an OIDC identity scope
|
|
func hasIdentityScope(scopes fosite.Arguments) bool {
|
|
return slices.ContainsFunc(scopes, isIdentityScope)
|
|
}
|
|
|
|
// withIdentityAudience returns a view of the requester whose granted audience additionally includes the issuer when the token carries an identity scope
|
|
// This lets an access token that was granted an identity scope be presented to Pocket ID's own identity endpoints such as /userinfo, even when it was also audienced to a custom API
|
|
// The issuer is added only to the materialized access token and never to the underlying grant, so a refresh does not have to re-whitelist the issuer against the client and a machine token whose identity scopes were stripped never receives it
|
|
func withIdentityAudience(requester fosite.Requester, issuer string) fosite.Requester {
|
|
if issuer == "" || !hasIdentityScope(requester.GetGrantedScopes()) {
|
|
return requester
|
|
}
|
|
|
|
granted := requester.GetGrantedAudience()
|
|
if granted.Has(issuer) {
|
|
return requester
|
|
}
|
|
|
|
audience := make(fosite.Arguments, 0, len(granted)+1)
|
|
audience = append(audience, granted...)
|
|
audience = append(audience, issuer)
|
|
return identityAudienceRequester{Requester: requester, grantedAudience: audience}
|
|
}
|
|
|
|
// identityAudienceRequester overrides only the granted audience of the wrapped requester
|
|
type identityAudienceRequester struct {
|
|
fosite.Requester
|
|
grantedAudience fosite.Arguments
|
|
}
|
|
|
|
func (r identityAudienceRequester) GetGrantedAudience() fosite.Arguments {
|
|
return r.grantedAudience
|
|
}
|
|
|
|
// identityAudienceAccessTokenStrategy wraps the access token strategy so an access token granted an identity scope also lists the issuer in its audience, keeping the self-contained JWT consistent with what is persisted for introspection and userinfo
|
|
type identityAudienceAccessTokenStrategy struct {
|
|
fositeoauth2.CoreStrategy
|
|
issuer string
|
|
}
|
|
|
|
func (s identityAudienceAccessTokenStrategy) GenerateAccessToken(ctx context.Context, requester fosite.Requester) (string, string, error) {
|
|
return s.CoreStrategy.GenerateAccessToken(ctx, withIdentityAudience(requester, s.issuer))
|
|
}
|