@@ -7,6 +7,7 @@ package auth
77import (
88 "context"
99 "net/http"
10+ "slices"
1011 "strings"
1112 "time"
1213
@@ -25,28 +26,75 @@ var (
2526 _ Method = & OAuth2 {}
2627)
2728
29+ // GrantAdditionalScopes returns valid scopes coming from grant
30+ func GrantAdditionalScopes (grantScopes string ) string {
31+ // process additional scopes only if enabled in settings
32+ // this could be removed once additional scopes get accepted
33+ if ! setting .OAuth2 .EnableAdditionalGrantScopes {
34+ return ""
35+ }
36+
37+ // scopes_supported from templates/user/auth/oidc_wellknown.tmpl
38+ scopesSupported := []string {
39+ "openid" ,
40+ "profile" ,
41+ "email" ,
42+ "groups" ,
43+ }
44+
45+ var apiTokenScopes []string
46+ for _ , apiTokenScope := range strings .Split (grantScopes , " " ) {
47+ if slices .Index (scopesSupported , apiTokenScope ) == - 1 {
48+ apiTokenScopes = append (apiTokenScopes , apiTokenScope )
49+ }
50+ }
51+
52+ if len (apiTokenScopes ) == 0 {
53+ return ""
54+ }
55+
56+ var additionalGrantScopes []string
57+ allScopes := auth_model .AccessTokenScope ("all" )
58+
59+ for _ , apiTokenScope := range apiTokenScopes {
60+ grantScope := auth_model .AccessTokenScope (apiTokenScope )
61+ if ok , _ := allScopes .HasScope (grantScope ); ok {
62+ additionalGrantScopes = append (additionalGrantScopes , apiTokenScope )
63+ } else if apiTokenScope == "public-only" {
64+ additionalGrantScopes = append (additionalGrantScopes , apiTokenScope )
65+ }
66+ }
67+ if len (additionalGrantScopes ) > 0 {
68+ return strings .Join (additionalGrantScopes , "," )
69+ }
70+
71+ return ""
72+ }
73+
2874// CheckOAuthAccessToken returns uid of user from oauth token
29- func CheckOAuthAccessToken (ctx context.Context , accessToken string ) int64 {
75+ // + non default openid scopes requested
76+ func CheckOAuthAccessToken (ctx context.Context , accessToken string ) (int64 , string ) {
3077 // JWT tokens require a "."
3178 if ! strings .Contains (accessToken , "." ) {
32- return 0
79+ return 0 , ""
3380 }
3481 token , err := oauth2_provider .ParseToken (accessToken , oauth2_provider .DefaultSigningKey )
3582 if err != nil {
3683 log .Trace ("oauth2.ParseToken: %v" , err )
37- return 0
84+ return 0 , ""
3885 }
3986 var grant * auth_model.OAuth2Grant
4087 if grant , err = auth_model .GetOAuth2GrantByID (ctx , token .GrantID ); err != nil || grant == nil {
41- return 0
88+ return 0 , ""
4289 }
4390 if token .Kind != oauth2_provider .KindAccessToken {
44- return 0
91+ return 0 , ""
4592 }
4693 if token .ExpiresAt .Before (time .Now ()) || token .IssuedAt .After (time .Now ()) {
47- return 0
94+ return 0 , ""
4895 }
49- return grant .UserID
96+ grantScopes := GrantAdditionalScopes (grant .Scope )
97+ return grant .UserID , grantScopes
5098}
5199
52100// OAuth2 implements the Auth interface and authenticates requests
@@ -92,10 +140,15 @@ func parseToken(req *http.Request) (string, bool) {
92140func (o * OAuth2 ) userIDFromToken (ctx context.Context , tokenSHA string , store DataStore ) int64 {
93141 // Let's see if token is valid.
94142 if strings .Contains (tokenSHA , "." ) {
95- uid := CheckOAuthAccessToken (ctx , tokenSHA )
143+ uid , grantScopes := CheckOAuthAccessToken (ctx , tokenSHA )
144+
96145 if uid != 0 {
97146 store .GetData ()["IsApiToken" ] = true
98- store .GetData ()["ApiTokenScope" ] = auth_model .AccessTokenScopeAll // fallback to all
147+ if grantScopes != "" {
148+ store .GetData ()["ApiTokenScope" ] = auth_model .AccessTokenScope (grantScopes )
149+ } else {
150+ store .GetData ()["ApiTokenScope" ] = auth_model .AccessTokenScopeAll // fallback to all
151+ }
99152 }
100153 return uid
101154 }
0 commit comments