141 lines
3.7 KiB
Go
141 lines
3.7 KiB
Go
package auth
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"encoding/json"
|
|
"fmt"
|
|
"net/http"
|
|
"net/url"
|
|
"strings"
|
|
"sync"
|
|
"time"
|
|
|
|
"fiskerinc.com/modules/httpclient"
|
|
"fiskerinc.com/modules/jwt"
|
|
"fiskerinc.com/modules/logger"
|
|
"fiskerinc.com/modules/utils/envtool"
|
|
"github.com/pkg/errors"
|
|
)
|
|
|
|
var cognitoUserConsentClientID = envtool.GetEnv("COGNITO_USER_CONSENT_CLIENT_ID", "REPLACE_ME")
|
|
var cognitoUserConsentClientSecret = envtool.GetEnv("COGNITO_USER_CONSENT_CLIENT_SECRET", "REPLACE_ME")
|
|
var cognitoUserConsentAuthURL = envtool.GetEnv("COGNITO_USER_CONSENT_AUTH_URL", "REPLACE_ME")
|
|
|
|
var CognitoUserConsentJWT CognitoJWT
|
|
|
|
type CognitoJWT struct {
|
|
token string
|
|
expiration *time.Time
|
|
mu sync.RWMutex
|
|
}
|
|
|
|
func (c *CognitoJWT) GetUserConsentToken() (string, error) {
|
|
c.mu.Lock()
|
|
defer c.mu.Unlock()
|
|
|
|
// check for an unexpired token. if it is there, return it
|
|
if c.token != "" && time.Now().Before(*c.expiration) {
|
|
return c.token, nil
|
|
}
|
|
|
|
// make a request to Cognito to get the token
|
|
authResponse, err := RequestUserConsentToken()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
|
|
// cache the token and expire time
|
|
c.token = authResponse.AccessToken.JWTToken
|
|
c.expiration = &authResponse.ExpireTime
|
|
|
|
return c.token, nil
|
|
}
|
|
|
|
func RequestUserConsentToken() (AuthResponse, error) {
|
|
var resp AuthTokens
|
|
var tokens AuthResponse
|
|
|
|
tokenReq, err := getUserConsentTokenRequest()
|
|
if err != nil {
|
|
return tokens, err
|
|
}
|
|
|
|
tokenRes, err := httpclient.Do(tokenReq)
|
|
if err != nil {
|
|
return tokens, err
|
|
}
|
|
defer tokenRes.Body.Close()
|
|
|
|
err = json.NewDecoder(tokenRes.Body).Decode(&resp)
|
|
if err != nil {
|
|
logger.Error().Err(err).Send()
|
|
}
|
|
|
|
if len(resp.Error) > 0 {
|
|
return tokens, errors.New(resp.Error)
|
|
}
|
|
|
|
return getAuthResponse(&resp), nil
|
|
}
|
|
|
|
// getUserConsentTokenRequest returns http request to exchange code for a user consent token
|
|
func getUserConsentTokenRequest() (*http.Request, error) {
|
|
basicAuth := base64.StdEncoding.EncodeToString([]byte(strings.Join([]string{cognitoUserConsentClientID, ":", cognitoUserConsentClientSecret}, "")))
|
|
|
|
body := url.Values{
|
|
"client_id": {cognitoUserConsentClientID},
|
|
"grant_type": {"client_credentials"},
|
|
}
|
|
|
|
r, err := http.NewRequest(http.MethodPost, getUserConsentTokenURL(), strings.NewReader(body.Encode()))
|
|
if err != nil {
|
|
return nil, errors.WithStack(err)
|
|
}
|
|
|
|
r.Header.Add("Authorization", fmt.Sprintf("Basic %s", basicAuth))
|
|
r.Header.Add("Content-type", "application/x-www-form-urlencoded")
|
|
|
|
return r, nil
|
|
}
|
|
|
|
// GetAuthResponse converts AuthTokens response from Cognito to include decoded payload
|
|
func getAuthResponse(data *AuthTokens) AuthResponse {
|
|
var result AuthResponse
|
|
|
|
result.AccessToken.JWTToken = data.AcessToken
|
|
result.IDToken.JWTToken = data.IDToken
|
|
result.RefreshToken.Token = data.RefreshToken
|
|
|
|
// calculate the expire time of the token, leaving off one minute of wiggle room
|
|
result.ExpireTime = time.Now().Add(time.Duration(data.ExpiresIn)*time.Second - time.Minute)
|
|
|
|
return result
|
|
}
|
|
|
|
func getUserConsentTokenURL() string {
|
|
return cognitoUserConsentAuthURL + "/oauth2/token"
|
|
}
|
|
|
|
// AuthTokens json response for auth tokens
|
|
type AuthTokens struct {
|
|
AcessToken string `json:"access_token"`
|
|
RefreshToken string `json:"refresh_token,omitempty"`
|
|
IDToken string `json:"id_token,omitempty"`
|
|
TokenType string `json:"token_type"`
|
|
ExpiresIn int64 `json:"expires_in"`
|
|
Error string `json:"error,omitempty"`
|
|
}
|
|
|
|
// JWTResponse json response
|
|
type JWTResponse struct {
|
|
JWTToken string `json:"jwtToken"`
|
|
}
|
|
|
|
// AuthResponse json response
|
|
type AuthResponse struct {
|
|
AccessToken JWTResponse `json:"accessToken,omitempty"`
|
|
IDToken JWTResponse `json:"idToken,omitempty"`
|
|
RefreshToken jwt.AuthToken `json:"refreshToken,omitempty"`
|
|
ExpireTime time.Time `json:"expire_time"`
|
|
}
|