You've already forked oauth2-proxy
							
							
				mirror of
				https://github.com/oauth2-proxy/oauth2-proxy.git
				synced 2025-10-30 23:47:52 +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:
		
							
								
								
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							
							
						
						
									
										1
									
								
								.gitignore
									
									
									
									
										vendored
									
									
								
							| @@ -17,6 +17,7 @@ c.out | ||||
| # Folders | ||||
| _obj | ||||
| _test | ||||
| .DS_Store | ||||
| .idea/ | ||||
| .vscode/* | ||||
| !/.vscode/tasks.json | ||||
|   | ||||
| @@ -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) | ||||
| - [#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 | ||||
|  | ||||
|   | ||||
| @@ -37,18 +37,17 @@ account is still authorized. | ||||
|  | ||||
| #### 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,  | ||||
|     alternatively download the JSON. | ||||
| 2.  Make note of the Client ID for a future step. | ||||
| 3.  Under "APIs & Auth", choose APIs. | ||||
| 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: | ||||
|  | ||||
|     ``` | ||||
|     https://www.googleapis.com/auth/admin.directory.group.readonly | ||||
|     https://www.googleapis.com/auth/admin.directory.user.readonly | ||||
|     https://www.googleapis.com/auth/admin.directory.group.member.readonly | ||||
|     ``` | ||||
|  | ||||
| 6.  Follow the steps on https://support.google.com/a/answer/60757 to enable Admin API access. | ||||
|   | ||||
| @@ -229,38 +229,79 @@ func (p *GoogleProvider) setGroupRestriction(opts options.GoogleOptions) { | ||||
| 	} | ||||
| } | ||||
|  | ||||
| func getAdminService(opts options.GoogleOptions) *admin.Service { | ||||
| 	ctx := context.Background() | ||||
| 	var client *http.Client | ||||
| // https://developers.google.com/admin-sdk/directory/reference/rest/v1/members/hasMember#authorization-scopes | ||||
| var possibleScopesList = [...]string{ | ||||
| 	admin.AdminDirectoryGroupMemberReadonlyScope, | ||||
| 	admin.AdminDirectoryGroupReadonlyScope, | ||||
| 	admin.AdminDirectoryGroupMemberScope, | ||||
| 	admin.AdminDirectoryGroupScope, | ||||
| } | ||||
|  | ||||
| func getOauth2TokenSource(ctx context.Context, opts options.GoogleOptions, scope string) oauth2.TokenSource { | ||||
| 	if opts.UseApplicationDefaultCredentials { | ||||
| 		ts, err := impersonate.CredentialsTokenSource(ctx, impersonate.CredentialsConfig{ | ||||
| 			TargetPrincipal: getTargetPrincipal(ctx, opts), | ||||
| 			Scopes:          []string{admin.AdminDirectoryGroupReadonlyScope, admin.AdminDirectoryUserReadonlyScope}, | ||||
| 			Scopes:          []string{scope}, | ||||
| 			Subject:         opts.AdminEmail, | ||||
| 		}) | ||||
| 		if err != nil { | ||||
| 			logger.Fatal("failed to fetch application default credentials: ", err) | ||||
| 		} | ||||
| 		client = oauth2.NewClient(ctx, ts) | ||||
| 	} else { | ||||
| 		credentialsReader, err := os.Open(opts.ServiceAccountJSON) | ||||
| 		if err != nil { | ||||
| 			logger.Fatal("couldn't open Google credentials file: ", err) | ||||
| 			return nil | ||||
| 		} | ||||
|  | ||||
| 		data, err := io.ReadAll(credentialsReader) | ||||
| 		if err != nil { | ||||
| 			logger.Fatal("can't read Google credentials file:", err) | ||||
| 		} | ||||
|  | ||||
| 		conf, err := google.JWTConfigFromJSON(data, admin.AdminDirectoryUserReadonlyScope, admin.AdminDirectoryGroupReadonlyScope) | ||||
| 		if err != nil { | ||||
| 			logger.Fatal("can't load Google credentials file:", err) | ||||
| 		} | ||||
| 		conf.Subject = opts.AdminEmail | ||||
| 		client = conf.Client(ctx) | ||||
| 		return ts | ||||
| 	} | ||||
|  | ||||
| 	credentialsReader, err := os.Open(opts.ServiceAccountJSON) | ||||
| 	if err != nil { | ||||
| 		logger.Fatal("couldn't open Google credentials file: ", err) | ||||
| 	} | ||||
|  | ||||
| 	data, err := io.ReadAll(credentialsReader) | ||||
| 	if err != nil { | ||||
| 		logger.Fatal("can't read Google credentials file:", err) | ||||
| 	} | ||||
|  | ||||
| 	conf, err := google.JWTConfigFromJSON(data, scope) | ||||
| 	if err != nil { | ||||
| 		logger.Fatal("can't load Google credentials file:", err) | ||||
| 	} | ||||
|  | ||||
| 	conf.Subject = opts.AdminEmail | ||||
| 	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)) | ||||
| 	if err != nil { | ||||
| 		logger.Fatal(err) | ||||
|   | ||||
		Reference in New Issue
	
	Block a user