1
0
mirror of https://github.com/oauth2-proxy/oauth2-proxy.git synced 2025-08-06 22:42:56 +02:00

feat: allow use more possible google admin-sdk api scopes (#2743)

* feat: Allow use more possible google admin-sdk api scopes.

* reduce cognitive complexity

Signed-off-by: Bob Du <i@bobdu.cc>

* remove unnecessary else block / indentation

Signed-off-by: Jan Larwig <jan@larwig.com>

* add changelog entry

Signed-off-by: Jan Larwig <jan@larwig.com>

* slight formatting and error message rephrasing

Signed-off-by: Jan Larwig <jan@larwig.com>

---------

Signed-off-by: Bob Du <i@bobdu.cc>
Signed-off-by: Jan Larwig <jan@larwig.com>
Co-authored-by: Jan Larwig <jan@larwig.com>
This commit is contained in:
Bob Du
2025-07-21 15:06:17 +08:00
committed by GitHub
parent 3ac834dbcf
commit 4d17bc1d68
4 changed files with 69 additions and 27 deletions

1
.gitignore vendored
View File

@ -17,6 +17,7 @@ c.out
# Folders # Folders
_obj _obj
_test _test
.DS_Store
.idea/ .idea/
.vscode/* .vscode/*
!/.vscode/tasks.json !/.vscode/tasks.json

View File

@ -10,6 +10,7 @@
- [#2615](https://github.com/oauth2-proxy/oauth2-proxy/pull/2615) feat(cookies): add option to set a limit on the number of per-request CSRF cookies oauth2-proxy sets (@bh-tt) - [#2615](https://github.com/oauth2-proxy/oauth2-proxy/pull/2615) feat(cookies): add option to set a limit on the number of per-request CSRF cookies oauth2-proxy sets (@bh-tt)
- [#2605](https://github.com/oauth2-proxy/oauth2-proxy/pull/2605) fix: show login page on broken cookie (@Primexz) - [#2605](https://github.com/oauth2-proxy/oauth2-proxy/pull/2605) fix: show login page on broken cookie (@Primexz)
- [#2743](https://github.com/oauth2-proxy/oauth2-proxy/pull/2743) feat: allow use more possible google admin-sdk api scopes (@BobDu)
# V7.10.0 # V7.10.0

View File

@ -37,18 +37,17 @@ account is still authorized.
#### Restrict auth to specific Google groups on your domain. (optional) #### Restrict auth to specific Google groups on your domain. (optional)
1. Create a [service account](https://developers.google.com/identity/protocols/OAuth2ServiceAccount) and configure it 1. Create a [service account](https://developers.google.com/identity/protocols/oauth2/service-account) and configure it
to use [Application Default Credentials / Workload Identity / Workload Identity Federation (recommended)](#using-application-default-credentials-adc--workload-identity--workload-identity-federation-recommended) or, to use [Application Default Credentials / Workload Identity / Workload Identity Federation (recommended)](#using-application-default-credentials-adc--workload-identity--workload-identity-federation-recommended) or,
alternatively download the JSON. alternatively download the JSON.
2. Make note of the Client ID for a future step. 2. Make note of the Client ID for a future step.
3. Under "APIs & Auth", choose APIs. 3. Under "APIs & Auth", choose APIs.
4. Click on Admin SDK and then Enable API. 4. Click on Admin SDK and then Enable API.
5. Follow the steps on https://developers.google.com/admin-sdk/directory/v1/guides/delegation#delegate_domain-wide_authority_to_your_service_account 5. Follow the steps on [Set up domain-wide delegation for a service account](https://developers.google.com/workspace/guides/create-credentials#optional_set_up_domain-wide_delegation_for_a_service_account)
and give the client id from step 2 the following oauth scopes: and give the client id from step 2 the following oauth scopes:
``` ```
https://www.googleapis.com/auth/admin.directory.group.readonly https://www.googleapis.com/auth/admin.directory.group.member.readonly
https://www.googleapis.com/auth/admin.directory.user.readonly
``` ```
6. Follow the steps on https://support.google.com/a/answer/60757 to enable Admin API access. 6. Follow the steps on https://support.google.com/a/answer/60757 to enable Admin API access.

View File

@ -229,24 +229,30 @@ func (p *GoogleProvider) setGroupRestriction(opts options.GoogleOptions) {
} }
} }
func getAdminService(opts options.GoogleOptions) *admin.Service { // https://developers.google.com/admin-sdk/directory/reference/rest/v1/members/hasMember#authorization-scopes
ctx := context.Background() var possibleScopesList = [...]string{
var client *http.Client admin.AdminDirectoryGroupMemberReadonlyScope,
admin.AdminDirectoryGroupReadonlyScope,
admin.AdminDirectoryGroupMemberScope,
admin.AdminDirectoryGroupScope,
}
func getOauth2TokenSource(ctx context.Context, opts options.GoogleOptions, scope string) oauth2.TokenSource {
if opts.UseApplicationDefaultCredentials { if opts.UseApplicationDefaultCredentials {
ts, err := impersonate.CredentialsTokenSource(ctx, impersonate.CredentialsConfig{ ts, err := impersonate.CredentialsTokenSource(ctx, impersonate.CredentialsConfig{
TargetPrincipal: getTargetPrincipal(ctx, opts), TargetPrincipal: getTargetPrincipal(ctx, opts),
Scopes: []string{admin.AdminDirectoryGroupReadonlyScope, admin.AdminDirectoryUserReadonlyScope}, Scopes: []string{scope},
Subject: opts.AdminEmail, Subject: opts.AdminEmail,
}) })
if err != nil { if err != nil {
logger.Fatal("failed to fetch application default credentials: ", err) logger.Fatal("failed to fetch application default credentials: ", err)
} }
client = oauth2.NewClient(ctx, ts) return ts
} else { }
credentialsReader, err := os.Open(opts.ServiceAccountJSON) credentialsReader, err := os.Open(opts.ServiceAccountJSON)
if err != nil { if err != nil {
logger.Fatal("couldn't open Google credentials file: ", err) logger.Fatal("couldn't open Google credentials file: ", err)
return nil
} }
data, err := io.ReadAll(credentialsReader) data, err := io.ReadAll(credentialsReader)
@ -254,13 +260,48 @@ func getAdminService(opts options.GoogleOptions) *admin.Service {
logger.Fatal("can't read Google credentials file:", err) logger.Fatal("can't read Google credentials file:", err)
} }
conf, err := google.JWTConfigFromJSON(data, admin.AdminDirectoryUserReadonlyScope, admin.AdminDirectoryGroupReadonlyScope) conf, err := google.JWTConfigFromJSON(data, scope)
if err != nil { if err != nil {
logger.Fatal("can't load Google credentials file:", err) logger.Fatal("can't load Google credentials file:", err)
} }
conf.Subject = opts.AdminEmail conf.Subject = opts.AdminEmail
client = conf.Client(ctx) return conf.TokenSource(ctx)
}
func getAdminService(opts options.GoogleOptions) *admin.Service {
ctx := context.Background()
var client *http.Client
for _, scope := range possibleScopesList {
ts := getOauth2TokenSource(ctx, opts, scope)
_, err := ts.Token()
if err == nil {
client = oauth2.NewClient(ctx, ts)
break
} }
if retrieveErr, ok := err.(*oauth2.RetrieveError); ok {
retrieveErrBody := map[string]interface{}{}
if err := json.Unmarshal(retrieveErr.Body, &retrieveErrBody); err != nil {
logger.Fatal("error unmarshalling retrieveErr body:", err)
}
if retrieveErrBody["error"] == "unauthorized_client" && retrieveErrBody["error_description"] == "Client is unauthorized to retrieve access tokens using this method, or client not authorized for any of the scopes requested." {
continue
}
logger.Fatal("error retrieving token:", err)
}
}
if client == nil {
logger.Fatal("error: google credentials do not have enough permissions to access admin API scope")
}
adminService, err := admin.NewService(ctx, option.WithHTTPClient(client)) adminService, err := admin.NewService(ctx, option.WithHTTPClient(client))
if err != nil { if err != nil {
logger.Fatal(err) logger.Fatal(err)