115 lines
2.7 KiB
Go
115 lines
2.7 KiB
Go
package auth
|
|
|
|
import (
|
|
"context"
|
|
"crypto/sha256"
|
|
"net/url"
|
|
|
|
"git.icedream.tech/icedream/oneandone-billing-mailer/pkg/appcontext"
|
|
"github.com/go-logr/logr"
|
|
"github.com/google/uuid"
|
|
"golang.org/x/oauth2"
|
|
"golang.org/x/oauth2/authhandler"
|
|
)
|
|
|
|
type OAuth struct {
|
|
config *oauth2.Config
|
|
ctx context.Context
|
|
pkceKey string
|
|
}
|
|
|
|
func NewOAuth(ctx context.Context, clientID string, clientSecret string, baseUri *url.URL) *OAuth {
|
|
endpoint := oauth2.Endpoint{
|
|
AuthURL: baseUri.ResolveReference(&url.URL{Path: "auth/init"}).String(),
|
|
TokenURL: baseUri.ResolveReference(&url.URL{Path: "token"}).String(),
|
|
}
|
|
|
|
config := &oauth2.Config{
|
|
ClientID: clientID,
|
|
ClientSecret: clientSecret,
|
|
Endpoint: endpoint,
|
|
RedirectURL: redirect_url,
|
|
Scopes: []string{"openid", "account_otk"},
|
|
}
|
|
|
|
return &OAuth{
|
|
ctx: ctx,
|
|
config: config,
|
|
}
|
|
}
|
|
|
|
func NewOAuthFromEnvironment(ctx context.Context) (*OAuth, error) {
|
|
appctx, err := appcontext.GetEnvironmentContext()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
baseURL, err := appctx.CentralLogin.BaseURL()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
clientID := client_id
|
|
clientSecret := clientSecretForEnvironment()
|
|
|
|
return NewOAuth(ctx, clientID, clientSecret, baseURL), nil
|
|
}
|
|
|
|
func (h *OAuth) randomKey() (string, error) {
|
|
pkceKeyUUID, err := uuid.NewRandom()
|
|
if err != nil {
|
|
return "", err
|
|
}
|
|
pkceKey := pkceKeyUUID.String()
|
|
return pkceKey, nil
|
|
}
|
|
|
|
func (h *OAuth) TokenSource(t *oauth2.Token, authHandler authhandler.AuthorizationHandler) (oauth2.TokenSource, error) {
|
|
log := logr.FromContextOrDiscard(h.ctx).WithName("OAuth")
|
|
|
|
log.V(2).Info("TokenSource was called")
|
|
|
|
if t != nil && len(t.RefreshToken) > 0 {
|
|
// Use a refresher based on given token's refresh token instead
|
|
return h.config.TokenSource(h.ctx, t), nil
|
|
}
|
|
|
|
// Generate state.
|
|
//
|
|
// Note that the Control Center app does not even pass a state at all, but
|
|
// the oauth server properly implements handling it.
|
|
//state, err := h.randomKey()
|
|
//if err != nil {
|
|
// return nil, err
|
|
//}
|
|
state := ""
|
|
|
|
// Generate pkce key (which becomes our code verifier)
|
|
if len(h.pkceKey) == 0 {
|
|
pkceKey, err := h.randomKey()
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
h.pkceKey = pkceKey
|
|
}
|
|
|
|
// Generate pkce key digest (which becomes our code challenge)
|
|
messageDigest := sha256.New()
|
|
messageDigest.Write([]byte(h.pkceKey))
|
|
messageDigestResult := messageDigest.Sum(nil)
|
|
messageDigestResultB64 := urlEncodingWithoutPadding.EncodeToString(messageDigestResult)
|
|
pkce := &authhandler.PKCEParams{
|
|
Challenge: messageDigestResultB64,
|
|
ChallengeMethod: "S256",
|
|
Verifier: h.pkceKey,
|
|
}
|
|
|
|
tok := authhandler.TokenSourceWithPKCE(
|
|
context.Background(),
|
|
h.config,
|
|
state,
|
|
authHandler,
|
|
pkce)
|
|
|
|
return tok, nil
|
|
}
|