-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathmain.go
169 lines (136 loc) · 4.31 KB
/
main.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
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
package oauth2
import (
"bytes"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
)
var (
// come default scopes when intating oauth
Scopes []string = []string{"profile", "email"}
// enums for oauth actions
OauthActionSignin string = "SIGNIN"
OauthActionAuthorize string = "AUTHORIZE"
OauthActionCreateAccount string = "CREATE_ACCOUNT"
)
// UserOauthToken is token information returned after requesting to fetch the access token
type UserOauthToken struct {
IDToken string `json:"id_token"`
ExpiresAt int `json:"expires_in"`
TokenType string `json:"token_type"`
AccessToken string `json:"access_token"`
RefreshToken string `json:"refresh_token"`
}
// OauthState is a struct that will represent the JWT payload for the oauth state
type OauthState struct {
Action string `json:"action"`
}
// Oauth returns a new the methods initate oauth witha IDP
type Oauth struct {
ClientID string
OauthURL string
TokenURL string
CallbackURL string
ClientSecret string
Scopes []string
}
// New returns the Oauth struct
func New(clientID, oauthURL, tokenURL, callbackURL, clientSecret string, scopes []string) *Oauth {
return &Oauth{
Scopes: scopes,
ClientID: clientID,
OauthURL: oauthURL,
TokenURL: tokenURL,
CallbackURL: callbackURL,
ClientSecret: clientSecret,
}
}
// CreateOauthURL returns a formatted oauth url
func (oauth *Oauth) CreateOauthURL(state string) (string, error) {
parsedOauthURL, err := url.Parse(oauth.OauthURL)
if err != nil {
return "", err
}
query := parsedOauthURL.Query()
query.Set("scope", strings.Join(oauth.Scopes, " "))
query.Set("redirect_uri", oauth.CallbackURL)
query.Set("client_id", oauth.ClientID)
query.Set("access_type", "offline")
query.Set("response_type", "code")
query.Set("prompt", "consent")
query.Set("state", state)
parsedOauthURL.RawQuery = query.Encode()
return parsedOauthURL.String(), nil
}
// FetchAccessToken fetches the access token when obtaining the access after the user has oauth from the oauth prodiver
func (oauth *Oauth) FetchAccessToken(accessCode string) (UserOauthToken, error) {
var oauthAccessTokenInfo UserOauthToken
var query url.Values = make(url.Values)
query.Set("client_secret", oauth.ClientSecret)
query.Set("grant_type", "authorization_code")
query.Set("redirect_uri", oauth.CallbackURL)
query.Set("client_id", oauth.ClientID)
query.Set("code_verifier", "")
query.Set("code", accessCode)
err := fetchOauthToken(oauth.TokenURL, query.Encode(), &oauthAccessTokenInfo)
if err != nil {
return oauthAccessTokenInfo, err
}
return oauthAccessTokenInfo, nil
}
// RefreshAccessToken requests for a new access token from the oauth provider
func (oauth *Oauth) RefreshAccessToken(refreshToken string) (UserOauthToken, error) {
var oauthAccessTokenInfo UserOauthToken
var query url.Values = make(url.Values)
query.Set("client_secret", oauth.ClientSecret)
query.Set("refresh_token", refreshToken)
query.Set("grant_type", "refresh_token")
query.Set("client_id", oauth.ClientID)
err := fetchOauthToken(oauth.TokenURL, query.Encode(), &oauthAccessTokenInfo)
if err != nil {
return oauthAccessTokenInfo, err
}
return oauthAccessTokenInfo, nil
}
// fetchOauthToken this a obstraction from making the request to the oauth provider
func fetchOauthToken(oauthTokenURL string, urlEncodedBody string, val interface{}) error {
payload := bytes.NewBuffer([]byte(urlEncodedBody))
request, err := http.NewRequest("POST", oauthTokenURL, payload)
request.Header.Set("Content-Type", "application/x-www-form-urlencoded")
if err != nil {
return err
}
client := &http.Client{}
response, err := client.Do(request)
if err != nil {
return err
}
defer response.Body.Close()
body, err := ioutil.ReadAll(response.Body)
if err != nil {
return err
}
if response.StatusCode != 200 {
if response.StatusCode == 404 {
return errors.New("oauth: error fetching token, token endpoint does not exist")
}
var badRequest struct {
Error string `json:"error"`
Description string `json:"error_description"`
}
err = json.Unmarshal(body, &badRequest)
if err != nil {
return err
}
return fmt.Errorf("oauth: error fetching token %v %v", badRequest.Error, badRequest.Description)
}
err = json.Unmarshal(body, &val)
if err != nil {
return err
}
return nil
}