mirror of
https://github.com/oauth2-proxy/oauth2-proxy.git
synced 2025-06-08 23:56:36 +02:00
Workload identity support (#2126)
* WIP: support for workload identity * WIP: bugfixes to support WI * Added support for Workload Identity * Added missing flag * Refactoring and typo * Updated CHANGELOG.md * Updated docs * Updated changelog * Improved readability and fixed codeclimate issues * Update CHANGELOG.md Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk> * Fixed if statement * Apply suggestions from code review Co-authored-by: Jan Larwig <jan@larwig.com> * Cleanup * Removed target principal * Removed references to target principal * Added docs * Fixed header anchor linking * Update auth.md * Updated generated code * Improved code * Fixed tests --------- Co-authored-by: Joel Speed <Joel.speed@hotmail.co.uk> Co-authored-by: Jan Larwig <jan@larwig.com>
This commit is contained in:
parent
738c09095b
commit
a6e8ec81e8
@ -22,6 +22,7 @@
|
|||||||
- [#1988](https://github.com/oauth2-proxy/oauth2-proxy/pull/1988) Ensure sign-in page background is uniform throughout the page
|
- [#1988](https://github.com/oauth2-proxy/oauth2-proxy/pull/1988) Ensure sign-in page background is uniform throughout the page
|
||||||
- [#2013](https://github.com/oauth2-proxy/oauth2-proxy/pull/2013) Upgrade alpine to version 3.17.2 and library dependencies (@miguelborges99)
|
- [#2013](https://github.com/oauth2-proxy/oauth2-proxy/pull/2013) Upgrade alpine to version 3.17.2 and library dependencies (@miguelborges99)
|
||||||
- [#2047](https://github.com/oauth2-proxy/oauth2-proxy/pull/2047) CVE-2022-41717: DoS in Go net/http may lead to DoS (@miguelborges99)
|
- [#2047](https://github.com/oauth2-proxy/oauth2-proxy/pull/2047) CVE-2022-41717: DoS in Go net/http may lead to DoS (@miguelborges99)
|
||||||
|
- [#2126](https://github.com/oauth2-proxy/oauth2-proxy/pull/2126) Added support for GKE Workload Identity (@kvanzuijlen)
|
||||||
- [#1921](https://github.com/oauth2-proxy/oauth2-proxy/pull/1921) Check jsonpath syntax before interpretation
|
- [#1921](https://github.com/oauth2-proxy/oauth2-proxy/pull/1921) Check jsonpath syntax before interpretation
|
||||||
- [#2025](https://github.com/oauth2-proxy/oauth2-proxy/pull/2025) Embed static stylesheets and dependencies
|
- [#2025](https://github.com/oauth2-proxy/oauth2-proxy/pull/2025) Embed static stylesheets and dependencies
|
||||||
|
|
||||||
|
@ -236,6 +236,7 @@ Valid time units are "ns", "us" (or "µs"), "ms", "s", "m", "h".
|
|||||||
| `group` | _[]string_ | Groups sets restrict logins to members of this google group |
|
| `group` | _[]string_ | Groups sets restrict logins to members of this google group |
|
||||||
| `adminEmail` | _string_ | AdminEmail is the google admin to impersonate for api calls |
|
| `adminEmail` | _string_ | AdminEmail is the google admin to impersonate for api calls |
|
||||||
| `serviceAccountJson` | _string_ | ServiceAccountJSON is the path to the service account json credentials |
|
| `serviceAccountJson` | _string_ | ServiceAccountJSON is the path to the service account json credentials |
|
||||||
|
| `useApplicationDefaultCredentials` | _bool_ | UseApplicationDefaultCredentials is a boolean whether to use Application Default Credentials instead of a ServiceAccountJSON |
|
||||||
|
|
||||||
### Header
|
### Header
|
||||||
|
|
||||||
|
@ -50,12 +50,22 @@ It's recommended to refresh sessions on a short interval (1h) with `cookie-refre
|
|||||||
|
|
||||||
#### 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 make sure to download the json file.
|
1. Create a [service account](https://developers.google.com/identity/protocols/OAuth2ServiceAccount) and download the json
|
||||||
|
file if you're not using [Application Default Credentials / Workload Identity / Workload Identity Federation (recommended)](#using-application-default-credentials-adc--workload-identity--workload-identity-federation-recommended).
|
||||||
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 and give the client id from step 2 the following oauth scopes:
|
5. Follow the steps on https://developers.google.com/admin-sdk/directory/v1/guides/delegation#delegate_domain-wide_authority_to_your_service_account and give the client id from step 2 the following oauth scopes:
|
||||||
|
|
||||||
|
|
||||||
|
##### Using Application Default Credentials (ADC) / Workload Identity / Workload Identity Federation (recommended)
|
||||||
|
oauth2-proxy can make use of [Application Default Credentials](https://cloud.google.com/docs/authentication/application-default-credentials).
|
||||||
|
When deployed within GCP, this means that it can automatically use the service account attached to the resource. When deployed to GKE, ADC
|
||||||
|
can be leveraged through a feature called Workload Identity. Follow Google's [guide](https://cloud.google.com/kubernetes-engine/docs/how-to/workload-identity)
|
||||||
|
to set up Workload Identity.
|
||||||
|
|
||||||
|
When deployed outside of GCP, [Workload Identity Federation](https://cloud.google.com/docs/authentication/provide-credentials-adc#wlif) might be an option.
|
||||||
|
|
||||||
```
|
```
|
||||||
https://www.googleapis.com/auth/admin.directory.group.readonly
|
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.user.readonly
|
||||||
|
@ -120,6 +120,7 @@ An example [oauth2-proxy.cfg](https://github.com/oauth2-proxy/oauth2-proxy/blob/
|
|||||||
| `--google-admin-email` | string | the google admin to impersonate for api calls | |
|
| `--google-admin-email` | string | the google admin to impersonate for api calls | |
|
||||||
| `--google-group` | string | restrict logins to members of this google group (may be given multiple times). | |
|
| `--google-group` | string | restrict logins to members of this google group (may be given multiple times). | |
|
||||||
| `--google-service-account-json` | string | the path to the service account json credentials | |
|
| `--google-service-account-json` | string | the path to the service account json credentials | |
|
||||||
|
| `--google-use-application-default-credentials` | bool | use application default credentials instead of service account json (i.e. GKE Workload Identity) | |
|
||||||
| `--htpasswd-file` | string | additionally authenticate against a htpasswd file. Entries must be created with `htpasswd -B` for bcrypt encryption | |
|
| `--htpasswd-file` | string | additionally authenticate against a htpasswd file. Entries must be created with `htpasswd -B` for bcrypt encryption | |
|
||||||
| `--htpasswd-user-group` | string \| list | the groups to be set on sessions for htpasswd users | |
|
| `--htpasswd-user-group` | string \| list | the groups to be set on sessions for htpasswd users | |
|
||||||
| `--http-address` | string | `[http://]<addr>:<port>` or `unix://<path>` to listen on for HTTP clients. Square brackets are required for ipv6 address, e.g. `http://[::1]:4180` | `"127.0.0.1:4180"` |
|
| `--http-address` | string | `[http://]<addr>:<port>` or `unix://<path>` to listen on for HTTP clients. Square brackets are required for ipv6 address, e.g. `http://[::1]:4180` | `"127.0.0.1:4180"` |
|
||||||
|
@ -70,6 +70,7 @@ func NewLegacyFlagSet() *pflag.FlagSet {
|
|||||||
flagSet.AddFlagSet(legacyHeadersFlagSet())
|
flagSet.AddFlagSet(legacyHeadersFlagSet())
|
||||||
flagSet.AddFlagSet(legacyServerFlagset())
|
flagSet.AddFlagSet(legacyServerFlagset())
|
||||||
flagSet.AddFlagSet(legacyProviderFlagSet())
|
flagSet.AddFlagSet(legacyProviderFlagSet())
|
||||||
|
flagSet.AddFlagSet(legacyGoogleFlagSet())
|
||||||
|
|
||||||
return flagSet
|
return flagSet
|
||||||
}
|
}
|
||||||
@ -496,6 +497,7 @@ type LegacyProvider struct {
|
|||||||
GoogleGroups []string `flag:"google-group" cfg:"google_group"`
|
GoogleGroups []string `flag:"google-group" cfg:"google_group"`
|
||||||
GoogleAdminEmail string `flag:"google-admin-email" cfg:"google_admin_email"`
|
GoogleAdminEmail string `flag:"google-admin-email" cfg:"google_admin_email"`
|
||||||
GoogleServiceAccountJSON string `flag:"google-service-account-json" cfg:"google_service_account_json"`
|
GoogleServiceAccountJSON string `flag:"google-service-account-json" cfg:"google_service_account_json"`
|
||||||
|
GoogleUseApplicationDefaultCredentials bool `flag:"google-use-application-default-credentials" cfg:"google_use_application_default_credentials"`
|
||||||
|
|
||||||
// These options allow for other providers besides Google, with
|
// These options allow for other providers besides Google, with
|
||||||
// potential overrides.
|
// potential overrides.
|
||||||
@ -549,9 +551,6 @@ func legacyProviderFlagSet() *pflag.FlagSet {
|
|||||||
flagSet.StringSlice("github-user", []string{}, "allow users with these usernames to login even if they do not belong to the specified org and team or collaborators (may be given multiple times)")
|
flagSet.StringSlice("github-user", []string{}, "allow users with these usernames to login even if they do not belong to the specified org and team or collaborators (may be given multiple times)")
|
||||||
flagSet.StringSlice("gitlab-group", []string{}, "restrict logins to members of this group (may be given multiple times)")
|
flagSet.StringSlice("gitlab-group", []string{}, "restrict logins to members of this group (may be given multiple times)")
|
||||||
flagSet.StringSlice("gitlab-project", []string{}, "restrict logins to members of this project (may be given multiple times) (eg `group/project=accesslevel`). Access level should be a value matching Gitlab access levels (see https://docs.gitlab.com/ee/api/members.html#valid-access-levels), defaulted to 20 if absent")
|
flagSet.StringSlice("gitlab-project", []string{}, "restrict logins to members of this project (may be given multiple times) (eg `group/project=accesslevel`). Access level should be a value matching Gitlab access levels (see https://docs.gitlab.com/ee/api/members.html#valid-access-levels), defaulted to 20 if absent")
|
||||||
flagSet.StringSlice("google-group", []string{}, "restrict logins to members of this google group (may be given multiple times).")
|
|
||||||
flagSet.String("google-admin-email", "", "the google admin to impersonate for api calls")
|
|
||||||
flagSet.String("google-service-account-json", "", "the path to the service account json credentials")
|
|
||||||
flagSet.String("client-id", "", "the OAuth Client ID: ie: \"123456.apps.googleusercontent.com\"")
|
flagSet.String("client-id", "", "the OAuth Client ID: ie: \"123456.apps.googleusercontent.com\"")
|
||||||
flagSet.String("client-secret", "", "the OAuth Client Secret")
|
flagSet.String("client-secret", "", "the OAuth Client Secret")
|
||||||
flagSet.String("client-secret-file", "", "the file with OAuth Client Secret")
|
flagSet.String("client-secret-file", "", "the file with OAuth Client Secret")
|
||||||
@ -592,6 +591,17 @@ func legacyProviderFlagSet() *pflag.FlagSet {
|
|||||||
return flagSet
|
return flagSet
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func legacyGoogleFlagSet() *pflag.FlagSet {
|
||||||
|
flagSet := pflag.NewFlagSet("google", pflag.ExitOnError)
|
||||||
|
|
||||||
|
flagSet.StringSlice("google-group", []string{}, "restrict logins to members of this google group (may be given multiple times).")
|
||||||
|
flagSet.String("google-admin-email", "", "the google admin to impersonate for api calls")
|
||||||
|
flagSet.String("google-service-account-json", "", "the path to the service account json credentials")
|
||||||
|
flagSet.String("google-use-application-default-credentials", "", "use application default credentials instead of service account json (i.e. GKE Workload Identity)")
|
||||||
|
|
||||||
|
return flagSet
|
||||||
|
}
|
||||||
|
|
||||||
func (l LegacyServer) convert() (Server, Server) {
|
func (l LegacyServer) convert() (Server, Server) {
|
||||||
appServer := Server{
|
appServer := Server{
|
||||||
BindAddress: l.HTTPAddress,
|
BindAddress: l.HTTPAddress,
|
||||||
@ -721,6 +731,7 @@ func (l *LegacyProvider) convert() (Providers, error) {
|
|||||||
Groups: l.GoogleGroups,
|
Groups: l.GoogleGroups,
|
||||||
AdminEmail: l.GoogleAdminEmail,
|
AdminEmail: l.GoogleAdminEmail,
|
||||||
ServiceAccountJSON: l.GoogleServiceAccountJSON,
|
ServiceAccountJSON: l.GoogleServiceAccountJSON,
|
||||||
|
UseApplicationDefaultCredentials: l.GoogleUseApplicationDefaultCredentials,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -189,6 +189,8 @@ type GoogleOptions struct {
|
|||||||
AdminEmail string `json:"adminEmail,omitempty"`
|
AdminEmail string `json:"adminEmail,omitempty"`
|
||||||
// ServiceAccountJSON is the path to the service account json credentials
|
// ServiceAccountJSON is the path to the service account json credentials
|
||||||
ServiceAccountJSON string `json:"serviceAccountJson,omitempty"`
|
ServiceAccountJSON string `json:"serviceAccountJson,omitempty"`
|
||||||
|
// UseApplicationDefaultCredentials is a boolean whether to use Application Default Credentials instead of a ServiceAccountJSON
|
||||||
|
UseApplicationDefaultCredentials bool `json:"useApplicationDefaultCredentials,omitempty"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type OIDCOptions struct {
|
type OIDCOptions struct {
|
||||||
|
@ -63,7 +63,7 @@ func TestGoogleGroupOptions(t *testing.T) {
|
|||||||
|
|
||||||
expected := errorMsg([]string{
|
expected := errorMsg([]string{
|
||||||
"missing setting: google-admin-email",
|
"missing setting: google-admin-email",
|
||||||
"missing setting: google-service-account-json"})
|
"missing setting: google-service-account-json or google-use-application-default-credentials"})
|
||||||
assert.Equal(t, expected, err.Error())
|
assert.Equal(t, expected, err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -76,7 +76,7 @@ func TestGoogleGroupInvalidFile(t *testing.T) {
|
|||||||
assert.NotEqual(t, nil, err)
|
assert.NotEqual(t, nil, err)
|
||||||
|
|
||||||
expected := errorMsg([]string{
|
expected := errorMsg([]string{
|
||||||
"invalid Google credentials file: file_doesnt_exist.json",
|
"Google credentials file not found: file_doesnt_exist.json",
|
||||||
})
|
})
|
||||||
assert.Equal(t, expected, err.Error())
|
assert.Equal(t, expected, err.Error())
|
||||||
}
|
}
|
||||||
|
@ -66,20 +66,32 @@ func validateProvider(provider options.Provider, providerIDs map[string]struct{}
|
|||||||
|
|
||||||
func validateGoogleConfig(provider options.Provider) []string {
|
func validateGoogleConfig(provider options.Provider) []string {
|
||||||
msgs := []string{}
|
msgs := []string{}
|
||||||
if len(provider.GoogleConfig.Groups) > 0 ||
|
|
||||||
provider.GoogleConfig.AdminEmail != "" ||
|
hasGoogleGroups := len(provider.GoogleConfig.Groups) >= 1
|
||||||
provider.GoogleConfig.ServiceAccountJSON != "" {
|
hasAdminEmail := provider.GoogleConfig.AdminEmail != ""
|
||||||
if len(provider.GoogleConfig.Groups) < 1 {
|
hasSAJSON := provider.GoogleConfig.ServiceAccountJSON != ""
|
||||||
|
useADC := provider.GoogleConfig.UseApplicationDefaultCredentials
|
||||||
|
|
||||||
|
if !hasGoogleGroups && !hasAdminEmail && !hasSAJSON && !useADC {
|
||||||
|
return msgs
|
||||||
|
}
|
||||||
|
|
||||||
|
if !hasGoogleGroups {
|
||||||
msgs = append(msgs, "missing setting: google-group")
|
msgs = append(msgs, "missing setting: google-group")
|
||||||
}
|
}
|
||||||
if provider.GoogleConfig.AdminEmail == "" {
|
if !hasAdminEmail {
|
||||||
msgs = append(msgs, "missing setting: google-admin-email")
|
msgs = append(msgs, "missing setting: google-admin-email")
|
||||||
}
|
}
|
||||||
if provider.GoogleConfig.ServiceAccountJSON == "" {
|
|
||||||
msgs = append(msgs, "missing setting: google-service-account-json")
|
_, err := os.Stat(provider.GoogleConfig.ServiceAccountJSON)
|
||||||
} else if _, err := os.Stat(provider.GoogleConfig.ServiceAccountJSON); err != nil {
|
if !useADC {
|
||||||
msgs = append(msgs, fmt.Sprintf("invalid Google credentials file: %s", provider.GoogleConfig.ServiceAccountJSON))
|
if !hasSAJSON {
|
||||||
|
msgs = append(msgs, "missing setting: google-service-account-json or google-use-application-default-credentials")
|
||||||
|
} else if err != nil {
|
||||||
|
msgs = append(msgs, fmt.Sprintf("Google credentials file not found: %s", provider.GoogleConfig.ServiceAccountJSON))
|
||||||
}
|
}
|
||||||
|
} else if hasSAJSON {
|
||||||
|
msgs = append(msgs, "invalid setting: can't use both google-service-account-json and google-use-application-default-credentials")
|
||||||
}
|
}
|
||||||
|
|
||||||
return msgs
|
return msgs
|
||||||
|
@ -8,6 +8,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"io"
|
"io"
|
||||||
|
"net/http"
|
||||||
"net/url"
|
"net/url"
|
||||||
"os"
|
"os"
|
||||||
"strings"
|
"strings"
|
||||||
@ -17,6 +18,7 @@ import (
|
|||||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
|
||||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
|
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger"
|
||||||
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
|
"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
|
||||||
|
"golang.org/x/oauth2"
|
||||||
"golang.org/x/oauth2/google"
|
"golang.org/x/oauth2/google"
|
||||||
admin "google.golang.org/api/admin/directory/v1"
|
admin "google.golang.org/api/admin/directory/v1"
|
||||||
"google.golang.org/api/googleapi"
|
"google.golang.org/api/googleapi"
|
||||||
@ -98,17 +100,13 @@ func NewGoogleProvider(p *ProviderData, opts options.GoogleOptions) (*GoogleProv
|
|||||||
},
|
},
|
||||||
}
|
}
|
||||||
|
|
||||||
if opts.ServiceAccountJSON != "" {
|
if opts.ServiceAccountJSON != "" || opts.UseApplicationDefaultCredentials {
|
||||||
file, err := os.Open(opts.ServiceAccountJSON)
|
|
||||||
if err != nil {
|
|
||||||
return nil, fmt.Errorf("invalid Google credentials file: %s", opts.ServiceAccountJSON)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Backwards compatibility with `--google-group` option
|
// Backwards compatibility with `--google-group` option
|
||||||
if len(opts.Groups) > 0 {
|
if len(opts.Groups) > 0 {
|
||||||
provider.setAllowedGroups(opts.Groups)
|
provider.setAllowedGroups(opts.Groups)
|
||||||
}
|
}
|
||||||
provider.setGroupRestriction(opts.Groups, opts.AdminEmail, file)
|
|
||||||
|
provider.setGroupRestriction(opts)
|
||||||
}
|
}
|
||||||
|
|
||||||
return provider, nil
|
return provider, nil
|
||||||
@ -214,13 +212,13 @@ func (p *GoogleProvider) EnrichSession(_ context.Context, s *sessions.SessionSta
|
|||||||
// account credentials.
|
// account credentials.
|
||||||
//
|
//
|
||||||
// TODO (@NickMeves) - Unit Test this OR refactor away from groupValidator func
|
// TODO (@NickMeves) - Unit Test this OR refactor away from groupValidator func
|
||||||
func (p *GoogleProvider) setGroupRestriction(groups []string, adminEmail string, credentialsReader io.Reader) {
|
func (p *GoogleProvider) setGroupRestriction(opts options.GoogleOptions) {
|
||||||
adminService := getAdminService(adminEmail, credentialsReader)
|
adminService := getAdminService(opts)
|
||||||
p.groupValidator = func(s *sessions.SessionState) bool {
|
p.groupValidator = func(s *sessions.SessionState) bool {
|
||||||
// Reset our saved Groups in case membership changed
|
// Reset our saved Groups in case membership changed
|
||||||
// This is used by `Authorize` on every request
|
// This is used by `Authorize` on every request
|
||||||
s.Groups = make([]string, 0, len(groups))
|
s.Groups = make([]string, 0, len(opts.Groups))
|
||||||
for _, group := range groups {
|
for _, group := range opts.Groups {
|
||||||
if userInGroup(adminService, group, s.Email) {
|
if userInGroup(adminService, group, s.Email) {
|
||||||
s.Groups = append(s.Groups, group)
|
s.Groups = append(s.Groups, group)
|
||||||
}
|
}
|
||||||
@ -229,19 +227,37 @@ func (p *GoogleProvider) setGroupRestriction(groups []string, adminEmail string,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func getAdminService(adminEmail string, credentialsReader io.Reader) *admin.Service {
|
func getAdminService(opts options.GoogleOptions) *admin.Service {
|
||||||
|
ctx := context.Background()
|
||||||
|
var client *http.Client
|
||||||
|
if opts.UseApplicationDefaultCredentials {
|
||||||
|
ts, err := google.FindDefaultCredentialsWithParams(ctx, google.CredentialsParams{
|
||||||
|
Subject: opts.AdminEmail,
|
||||||
|
Scopes: []string{admin.AdminDirectoryGroupReadonlyScope, admin.AdminDirectoryUserReadonlyScope},
|
||||||
|
})
|
||||||
|
if err != nil {
|
||||||
|
logger.Fatal("failed to fetch application default credentials: ", err)
|
||||||
|
}
|
||||||
|
client = oauth2.NewClient(ctx, ts.TokenSource)
|
||||||
|
} 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)
|
data, err := io.ReadAll(credentialsReader)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
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, admin.AdminDirectoryUserReadonlyScope, admin.AdminDirectoryGroupReadonlyScope)
|
||||||
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 = adminEmail
|
conf.Subject = opts.AdminEmail
|
||||||
|
client = conf.Client(ctx)
|
||||||
ctx := context.Background()
|
}
|
||||||
client := conf.Client(ctx)
|
|
||||||
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)
|
||||||
|
Loading…
x
Reference in New Issue
Block a user