@@ -6,6 +6,8 @@ package oauth2_provider //nolint
66import (
77 "context"
88 "fmt"
9+ "slices"
10+ "strings"
911
1012 auth "code.gitea.io/gitea/models/auth"
1113 org_model "code.gitea.io/gitea/models/organization"
@@ -68,6 +70,59 @@ type AccessTokenResponse struct {
6870 IDToken string `json:"id_token,omitempty"`
6971}
7072
73+ // GrantAdditionalScopes returns valid scopes coming from grant
74+ func GrantAdditionalScopes (grantScopes string ) string {
75+ // scopes_supported from templates/user/auth/oidc_wellknown.tmpl
76+ scopesSupported := []string {
77+ "openid" ,
78+ "profile" ,
79+ "email" ,
80+ "groups" ,
81+ }
82+
83+ var apiTokenScopes []string
84+ for _ , apiTokenScope := range strings .Split (grantScopes , " " ) {
85+ if slices .Index (scopesSupported , apiTokenScope ) == - 1 {
86+ apiTokenScopes = append (apiTokenScopes , apiTokenScope )
87+ }
88+ }
89+
90+ if len (apiTokenScopes ) == 0 {
91+ return ""
92+ }
93+
94+ var additionalGrantScopes []string
95+ allScopes := auth .AccessTokenScope ("all" )
96+
97+ for _ , apiTokenScope := range apiTokenScopes {
98+ grantScope := auth .AccessTokenScope (apiTokenScope )
99+ if ok , _ := allScopes .HasScope (grantScope ); ok {
100+ additionalGrantScopes = append (additionalGrantScopes , apiTokenScope )
101+ } else if apiTokenScope == "public-only" {
102+ additionalGrantScopes = append (additionalGrantScopes , apiTokenScope )
103+ }
104+ }
105+ if len (additionalGrantScopes ) > 0 {
106+ return strings .Join (additionalGrantScopes , "," )
107+ }
108+
109+ return ""
110+ }
111+
112+ // check if groups are public only
113+ func IfOnlyPublicGroups (scopes string ) bool {
114+ tokenScopes := GrantAdditionalScopes (scopes )
115+ accessTokenScope , err := auth .AccessTokenScope (tokenScopes ).Normalize ()
116+ if err != nil {
117+ return true
118+ }
119+ requiredScopes := auth .GetRequiredScopes (auth .Read , auth .AccessTokenScopeCategoryRepository , auth .AccessTokenScopeCategoryOrganization )
120+ if private , _ := accessTokenScope .HasAnyScope (requiredScopes ... ); private {
121+ return false
122+ }
123+ return true
124+ }
125+
71126func NewAccessTokenResponse (ctx context.Context , grant * auth.OAuth2Grant , serverKey , clientKey JWTSigningKey ) (* AccessTokenResponse , * AccessTokenError ) {
72127 if setting .OAuth2 .InvalidateRefreshTokens {
73128 if err := grant .IncreaseCounter (ctx ); err != nil {
@@ -160,7 +215,9 @@ func NewAccessTokenResponse(ctx context.Context, grant *auth.OAuth2Grant, server
160215 idToken .EmailVerified = user .IsActive
161216 }
162217 if grant .ScopeContains ("groups" ) {
163- groups , err := GetOAuthGroupsForUser (ctx , user )
218+ onlyPublicGroups := IfOnlyPublicGroups (grant .Scope )
219+
220+ groups , err := GetOAuthGroupsForUser (ctx , user , onlyPublicGroups )
164221 if err != nil {
165222 log .Error ("Error getting groups: %v" , err )
166223 return nil , & AccessTokenError {
@@ -191,14 +248,26 @@ func NewAccessTokenResponse(ctx context.Context, grant *auth.OAuth2Grant, server
191248
192249// returns a list of "org" and "org:team" strings,
193250// that the given user is a part of.
194- func GetOAuthGroupsForUser (ctx context.Context , user * user_model.User ) ([]string , error ) {
251+ func GetOAuthGroupsForUser (ctx context.Context , user * user_model.User , onlyPublicGroups bool ) ([]string , error ) {
195252 orgs , err := org_model .GetUserOrgsList (ctx , user )
196253 if err != nil {
197254 return nil , fmt .Errorf ("GetUserOrgList: %w" , err )
198255 }
199256
200257 var groups []string
201258 for _ , org := range orgs {
259+ // process additional scopes only if enabled in settings
260+ // this could be removed once additional scopes get accepted
261+ if setting .OAuth2 .EnableAdditionalGrantScopes {
262+ if onlyPublicGroups {
263+ if public , err := org_model .IsPublicMembership (ctx , org .ID , user .ID ); err == nil {
264+ if ! public || ! org .Visibility .IsPublic () {
265+ continue
266+ }
267+ }
268+ }
269+ }
270+
202271 groups = append (groups , org .Name )
203272 teams , err := org .LoadTeams (ctx )
204273 if err != nil {
0 commit comments