mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-07-16 04:03:47 +03:00
refactor: move Pocket ID specific logic from Fosite into Pocket ID repo
This commit is contained in:
@@ -248,4 +248,4 @@ require (
|
|||||||
modernc.org/memory v1.11.0 // indirect
|
modernc.org/memory v1.11.0 // indirect
|
||||||
)
|
)
|
||||||
|
|
||||||
replace github.com/ory/fosite => github.com/pocket-id/fosite v0.0.0-20260706141717-ee376ba4821b
|
replace github.com/ory/fosite => github.com/pocket-id/fosite v0.0.0-20260706205234-b54f41b16dc0
|
||||||
|
|||||||
@@ -426,8 +426,8 @@ github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINE
|
|||||||
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
|
||||||
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
|
||||||
github.com/pocket-id/fosite v0.0.0-20260706141717-ee376ba4821b h1:/y/hIU6OzNiFWFd2eXSAR4wgbgU592TP5hXuszoPPjY=
|
github.com/pocket-id/fosite v0.0.0-20260706205234-b54f41b16dc0 h1:eD/P6k1xIxWDHXXGl4b7MyGBq4T1uq6w4DEWNuUHJew=
|
||||||
github.com/pocket-id/fosite v0.0.0-20260706141717-ee376ba4821b/go.mod h1:KeQ7tTIBm3DyeBnKcKLnPbSdrd6ttM6w3TD3yy9x8rM=
|
github.com/pocket-id/fosite v0.0.0-20260706205234-b54f41b16dc0/go.mod h1:KeQ7tTIBm3DyeBnKcKLnPbSdrd6ttM6w3TD3yy9x8rM=
|
||||||
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
|
||||||
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
|
||||||
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
|
||||||
|
|||||||
@@ -8,13 +8,14 @@ import (
|
|||||||
"gorm.io/gorm"
|
"gorm.io/gorm"
|
||||||
)
|
)
|
||||||
|
|
||||||
type SubjectType = fosite.ResourceIndicatorSubjectType
|
// SubjectType identifies the subject the requested access token will represent
|
||||||
|
type SubjectType string
|
||||||
|
|
||||||
const (
|
const (
|
||||||
// SubjectTypeUser covers user-delegated access: every flow whose access token acts on behalf of an end user
|
// SubjectTypeUser covers user-delegated access: every flow whose access token acts on behalf of an end user
|
||||||
SubjectTypeUser = fosite.ResourceOwnerSubject
|
SubjectTypeUser SubjectType = "user"
|
||||||
// SubjectTypeClient covers client access: the client credentials grant, where the client acts as itself without a user
|
// SubjectTypeClient covers client access: the client credentials grant, where the client acts as itself without a user
|
||||||
SubjectTypeClient = fosite.ClientSubject
|
SubjectTypeClient SubjectType = "client"
|
||||||
)
|
)
|
||||||
|
|
||||||
var standardScopes = fosite.Arguments{"openid", "profile", "email", "groups", "offline_access"}
|
var standardScopes = fosite.Arguments{"openid", "profile", "email", "groups", "offline_access"}
|
||||||
@@ -42,32 +43,65 @@ type APIAccessProvider interface {
|
|||||||
DescribePermissions(ctx context.Context, audience string, keys []string) ([]PermissionInfo, error)
|
DescribePermissions(ctx context.Context, audience string, keys []string) ([]PermissionInfo, error)
|
||||||
}
|
}
|
||||||
|
|
||||||
type resourceAccessProvider struct {
|
|
||||||
provider APIAccessProvider
|
|
||||||
}
|
|
||||||
|
|
||||||
func fositeResourceAccess(provider APIAccessProvider) fosite.ResourceIndicatorAccessProvider {
|
|
||||||
if provider == nil {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
return resourceAccessProvider{provider: provider}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (p resourceAccessProvider) AllowedScopesForResource(ctx context.Context, client fosite.Client, resource string, subjectType fosite.ResourceIndicatorSubjectType) (fosite.Arguments, bool, error) {
|
|
||||||
scopes, exists, err := p.provider.AllowedScopesForAudience(ctx, client.GetID(), resource, subjectType)
|
|
||||||
return scopes, exists, err
|
|
||||||
}
|
|
||||||
|
|
||||||
// resolveResource maps an RFC 8707 resource, which may be empty, to the audience to stamp on the issued token and the subset of requestedScopes that may be granted
|
// resolveResource maps an RFC 8707 resource, which may be empty, to the audience to stamp on the issued token and the subset of requestedScopes that may be granted
|
||||||
// An empty resource is a plain login token bound to the requesting client and yields only identity scopes
|
// An empty resource is a plain login token bound to the requesting client and yields only identity scopes
|
||||||
// The subject type selects which of the client's grants apply: user-delegated flows only see user grants, the client credentials grant only sees client grants
|
// The subject type selects which of the client's grants apply: user-delegated flows only see user grants, the client credentials grant only sees client grants
|
||||||
func resolveResource(ctx context.Context, provider APIAccessProvider, clientID, resource string, requestedScopes []string, subjectType SubjectType) (audience string, grantedScopes []string, err error) {
|
func resolveResource(ctx context.Context, provider APIAccessProvider, clientID, resource string, requestedScopes []string, subjectType SubjectType) (audience string, grantedScopes []string, err error) {
|
||||||
grant, err := fosite.ResolveResourceIndicatorGrant(ctx, fositeResourceAccess(provider), &fosite.DefaultClient{ID: clientID}, resource, requestedScopes, standardScopes, subjectType)
|
grantable := make(map[string]struct{}, len(standardScopes))
|
||||||
if err != nil {
|
for _, scope := range standardScopes {
|
||||||
return "", nil, err
|
grantable[scope] = struct{}{}
|
||||||
}
|
}
|
||||||
|
|
||||||
return grant.Audience, grant.Scopes, nil
|
if resource == "" {
|
||||||
|
// A plain login token is audienced to the requesting client
|
||||||
|
audience = clientID
|
||||||
|
} else {
|
||||||
|
if !fosite.IsValidResourceIndicatorURI(resource) || provider == nil {
|
||||||
|
return "", nil, fosite.ErrInvalidTarget.WithHintf("The requested resource '%s' is invalid, missing, unknown, or malformed.", resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
allowedScopes, apiExists, err := provider.AllowedScopesForAudience(ctx, clientID, resource, subjectType)
|
||||||
|
if err != nil {
|
||||||
|
return "", nil, err
|
||||||
|
}
|
||||||
|
if !apiExists {
|
||||||
|
return "", nil, fosite.ErrInvalidTarget.WithHintf("The requested resource '%s' is invalid, missing, unknown, or malformed.", resource)
|
||||||
|
}
|
||||||
|
if len(allowedScopes) == 0 {
|
||||||
|
return "", nil, fosite.ErrAccessDenied.WithHintf("The OAuth 2.0 Client is not allowed to access resource '%s'.", resource)
|
||||||
|
}
|
||||||
|
|
||||||
|
audience = resource
|
||||||
|
for _, scope := range allowedScopes {
|
||||||
|
grantable[scope] = struct{}{}
|
||||||
|
}
|
||||||
|
// A client credentials request without explicit scopes gets everything the client is granted for the API
|
||||||
|
if subjectType == SubjectTypeClient && len(requestedScopes) == 0 {
|
||||||
|
requestedScopes = allowedScopes
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
granted := make(fosite.Arguments, 0, len(requestedScopes))
|
||||||
|
for _, scope := range requestedScopes {
|
||||||
|
if _, ok := grantable[scope]; !ok {
|
||||||
|
return "", nil, fosite.ErrInvalidScope.WithHintf("The scope '%s' is not available for the requested resource.", scope)
|
||||||
|
}
|
||||||
|
if !granted.Has(scope) {
|
||||||
|
granted = append(granted, scope)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return audience, granted, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// grantResourceIndicator applies a resolved resource audience and scopes to the request
|
||||||
|
func grantResourceIndicator(requester fosite.Requester, audience string, scopes []string) {
|
||||||
|
for _, scope := range scopes {
|
||||||
|
requester.GrantScope(scope)
|
||||||
|
}
|
||||||
|
if audience != "" {
|
||||||
|
requester.GrantAudience(audience)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// consentScopeKey qualifies a custom-API scope by its audience so the same permission key on two different APIs is consented to separately
|
// consentScopeKey qualifies a custom-API scope by its audience so the same permission key on two different APIs is consented to separately
|
||||||
|
|||||||
@@ -270,7 +270,7 @@ func (s *authorizationService) authorizeAuthenticated(ctx context.Context, req a
|
|||||||
|
|
||||||
session := s.buildAuthorizedSession(req, interactionSession, authenticationTime)
|
session := s.buildAuthorizedSession(req, interactionSession, authenticationTime)
|
||||||
|
|
||||||
req.requester.GrantResourceIndicator(audience, grantedScopes)
|
grantResourceIndicator(req.requester, audience, grantedScopes)
|
||||||
|
|
||||||
authorizationEvent := model.AuditLogEventClientAuthorization
|
authorizationEvent := model.AuditLogEventClientAuthorization
|
||||||
if !hasAlreadyAuthorizedClient {
|
if !hasAlreadyAuthorizedClient {
|
||||||
|
|||||||
@@ -96,7 +96,7 @@ func (s *deviceService) acceptDeviceCode(ctx context.Context, userCode, userID,
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
request.GrantResourceIndicator(audience, grantedScopes)
|
grantResourceIndicator(request, audience, grantedScopes)
|
||||||
|
|
||||||
return withTx(ctx, s.db, func(ctx context.Context) error {
|
return withTx(ctx, s.db, func(ctx context.Context) error {
|
||||||
if client.RequiresReauthentication {
|
if client.RequiresReauthentication {
|
||||||
|
|||||||
@@ -74,7 +74,7 @@ func (h *tokenHandler) token(c *gin.Context) {
|
|||||||
accessReq.GrantedScope = grantedScopes
|
accessReq.GrantedScope = grantedScopes
|
||||||
accessReq.GrantedAudience = nil
|
accessReq.GrantedAudience = nil
|
||||||
}
|
}
|
||||||
accessRequest.GrantResourceIndicator(audience, grantedScopes)
|
grantResourceIndicator(accessRequest, audience, grantedScopes)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user