File: externalaccountauthorizeduser.go

package info (click to toggle)
golang-golang-x-oauth2 0.15.0-1~bpo12%2B1
  • links: PTS, VCS
  • area: main
  • in suites: bookworm-backports
  • size: 748 kB
  • sloc: makefile: 15
file content (114 lines) | stat: -rw-r--r-- 3,788 bytes parent folder | download | duplicates (5)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// Copyright 2023 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package externalaccountauthorizeduser

import (
	"context"
	"errors"
	"time"

	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google/internal/stsexchange"
)

// now aliases time.Now for testing.
var now = func() time.Time {
	return time.Now().UTC()
}

var tokenValid = func(token oauth2.Token) bool {
	return token.Valid()
}

type Config struct {
	// Audience is the Secure Token Service (STS) audience which contains the resource name for the workforce pool and
	// the provider identifier in that pool.
	Audience string
	// RefreshToken is the optional OAuth 2.0 refresh token. If specified, credentials can be refreshed.
	RefreshToken string
	// TokenURL is the optional STS token exchange endpoint for refresh. Must be specified for refresh, can be left as
	// None if the token can not be refreshed.
	TokenURL string
	// TokenInfoURL is the optional STS endpoint URL for token introspection.
	TokenInfoURL string
	// ClientID is only required in conjunction with ClientSecret, as described above.
	ClientID string
	// ClientSecret is currently only required if token_info endpoint also needs to be called with the generated GCP
	// access token. When provided, STS will be called with additional basic authentication using client_id as username
	// and client_secret as password.
	ClientSecret string
	// Token is the OAuth2.0 access token. Can be nil if refresh information is provided.
	Token string
	// Expiry is the optional expiration datetime of the OAuth 2.0 access token.
	Expiry time.Time
	// RevokeURL is the optional STS endpoint URL for revoking tokens.
	RevokeURL string
	// QuotaProjectID is the optional project ID used for quota and billing. This project may be different from the
	// project used to create the credentials.
	QuotaProjectID string
	Scopes         []string
}

func (c *Config) canRefresh() bool {
	return c.ClientID != "" && c.ClientSecret != "" && c.RefreshToken != "" && c.TokenURL != ""
}

func (c *Config) TokenSource(ctx context.Context) (oauth2.TokenSource, error) {
	var token oauth2.Token
	if c.Token != "" && !c.Expiry.IsZero() {
		token = oauth2.Token{
			AccessToken: c.Token,
			Expiry:      c.Expiry,
			TokenType:   "Bearer",
		}
	}
	if !tokenValid(token) && !c.canRefresh() {
		return nil, errors.New("oauth2/google: Token should be created with fields to make it valid (`token` and `expiry`), or fields to allow it to refresh (`refresh_token`, `token_url`, `client_id`, `client_secret`).")
	}

	ts := tokenSource{
		ctx:  ctx,
		conf: c,
	}

	return oauth2.ReuseTokenSource(&token, ts), nil
}

type tokenSource struct {
	ctx  context.Context
	conf *Config
}

func (ts tokenSource) Token() (*oauth2.Token, error) {
	conf := ts.conf
	if !conf.canRefresh() {
		return nil, errors.New("oauth2/google: The credentials do not contain the necessary fields need to refresh the access token. You must specify refresh_token, token_url, client_id, and client_secret.")
	}

	clientAuth := stsexchange.ClientAuthentication{
		AuthStyle:    oauth2.AuthStyleInHeader,
		ClientID:     conf.ClientID,
		ClientSecret: conf.ClientSecret,
	}

	stsResponse, err := stsexchange.RefreshAccessToken(ts.ctx, conf.TokenURL, conf.RefreshToken, clientAuth, nil)
	if err != nil {
		return nil, err
	}
	if stsResponse.ExpiresIn < 0 {
		return nil, errors.New("oauth2/google: got invalid expiry from security token service")
	}

	if stsResponse.RefreshToken != "" {
		conf.RefreshToken = stsResponse.RefreshToken
	}

	token := &oauth2.Token{
		AccessToken: stsResponse.AccessToken,
		Expiry:      now().Add(time.Duration(stsResponse.ExpiresIn) * time.Second),
		TokenType:   "Bearer",
	}
	return token, nil
}