This repository has been archived by the owner on Mar 17, 2022. It is now read-only.
/
client.go
131 lines (105 loc) · 3.26 KB
/
client.go
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
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
package client
import (
"crypto/tls"
"fmt"
"net/http"
"net/url"
jwtgo "github.com/dgrijalva/jwt-go"
"github.com/giantswarm/gscliauth/oidc"
gsclient "github.com/giantswarm/gsclientgen/client"
"github.com/giantswarm/microerror"
"github.com/go-openapi/runtime"
httptransport "github.com/go-openapi/runtime/client"
"github.com/go-openapi/strfmt"
)
// Client is our API client.
type Client struct {
APIEndpointURL string
IDToken *oidc.IDToken
AccessToken string
RefreshToken string
GSClientGen *gsclient.Gsclientgen
}
// New returns a fully conifgured API client and initiates the browser auth flow.
func New(endpointURL string) (*Client, error) {
u, err := url.Parse(endpointURL)
if err != nil {
return nil, microerror.New("invalid endpoint URL")
}
tlsConfig := &tls.Config{}
transport := httptransport.New(u.Host, "", []string{u.Scheme})
transport.Transport = &http.Transport{
Proxy: http.ProxyFromEnvironment,
TLSClientConfig: tlsConfig,
}
c := &Client{
APIEndpointURL: endpointURL,
GSClientGen: gsclient.New(transport, strfmt.Default),
}
pkceResponse, err := oidc.RunPKCE(endpointURL)
if err != nil {
fmt.Println("DEBUG: Attempt to run the OAuth2 PKCE workflow with a local callback HTTP server failed.")
return nil, microerror.Mask(err)
}
idToken, err := oidc.ParseIDToken(pkceResponse.IDToken)
if err != nil {
return nil, microerror.Mask(err)
}
// store tokens
c.IDToken = idToken
c.AccessToken = pkceResponse.AccessToken
c.RefreshToken = pkceResponse.RefreshToken
return c, nil
}
// AuthHeaderWriter returns a function to write an authentication header.
func (c *Client) AuthHeaderWriter() (runtime.ClientAuthInfoWriter, error) {
authHeader := "Bearer " + c.MustGetToken()
return httptransport.APIKeyAuth("Authorization", "header", authHeader), nil
}
// GetToken returns a token to use for the API client's Authorization header.
// If necessary, refreshes the token.
func (c *Client) GetToken() (string, error) {
// Check if it has a refresh token.
if c.RefreshToken == "" {
return "", microerror.New("No refresh token saved in config file, unable to acquire new access token. Please login again.")
}
if isTokenExpired(c.AccessToken) {
// Get a new token.
refreshTokenResponse, err := oidc.RefreshToken(c.RefreshToken)
if err != nil {
return "", microerror.Mask(err)
}
// Parse the ID Token for the email address.
idToken, err := oidc.ParseIDToken(refreshTokenResponse.IDToken)
if err != nil {
return "", microerror.Mask(err)
}
// Update the config file with the new access token.
c.IDToken = idToken
c.AccessToken = refreshTokenResponse.AccessToken
fmt.Println("DEBUG: Access token has just been refreshed.")
}
return c.AccessToken, nil
}
// MustGetToken returns a token and only prints errors.
func (c *Client) MustGetToken() string {
token, err := c.GetToken()
if err != nil {
fmt.Printf("ERROR: Could not get auth token. %s\n", err.Error())
}
return token
}
// isTokenExpired checks whwther this token is expired.
func isTokenExpired(token string) bool {
// Parse token
claims := jwtgo.MapClaims{}
parsedToken, _, err := new(jwtgo.Parser).ParseUnverified(token, claims)
if err != nil {
return true
}
err = parsedToken.Claims.Valid()
if err != nil {
return true
}
return false
}