From cc873b529c51a03340ea89bc73168a75beb63d27 Mon Sep 17 00:00:00 2001 From: ricoberger Date: Sun, 5 Apr 2020 12:25:01 +0200 Subject: [PATCH] Add support for OIDC --- Makefile | 2 ++ request/request.go | 74 +++++++++++++++++++++++++++++++++++++++++ request/request_test.go | 44 ++++++++++++++++++++++++ 3 files changed, 120 insertions(+) diff --git a/Makefile b/Makefile index 66f8210..92c938b 100644 --- a/Makefile +++ b/Makefile @@ -15,6 +15,8 @@ dependencies: GO111MODULE=off go get -u golang.org/x/mobile/cmd/gomobile GO111MODULE=off go get -u github.com/aws/aws-sdk-go/... GO111MODULE=off go get -u github.com/Azure/azure-sdk-for-go/services/containerservice/mgmt/2020-01-01/containerservice + GO111MODULE=off go get -u github.com/coreos/go-oidc + GO111MODULE=off go get -u golang.org/x/oauth2 GO111MODULE=off go get -u gopkg.in/yaml.v2 release-major: diff --git a/request/request.go b/request/request.go index 1fb66f1..cd746cd 100644 --- a/request/request.go +++ b/request/request.go @@ -21,6 +21,8 @@ import ( "github.com/aws/aws-sdk-go/aws/session" "github.com/aws/aws-sdk-go/service/eks" "github.com/aws/aws-sdk-go/service/sts" + "github.com/coreos/go-oidc" + "golang.org/x/oauth2" "gopkg.in/yaml.v2" ) @@ -281,3 +283,75 @@ func convert(i interface{}) interface{} { } return i } + +// OIDCGetLink returns the link for the configured OIDC provider. The Link can then be used by the user to login. +func OIDCGetLink(discoveryURL, clientID, clientSecret, redirectURL string) (string, error) { + ctx := context.Background() + provider, err := oidc.NewProvider(ctx, discoveryURL) + if err != nil { + return "", err + } + + oauth2Config := oauth2.Config{ + ClientID: clientID, + ClientSecret: clientSecret, + RedirectURL: redirectURL, + Endpoint: provider.Endpoint(), + Scopes: []string{oidc.ScopeOpenID}, + } + + return fmt.Sprintf("{\"url\": \"%s\"}", oauth2Config.AuthCodeURL("", oauth2.AccessTypeOffline, oauth2.ApprovalForce)), nil +} + +func OIDCGetRefreshToken(discoveryURL, clientID, clientSecret, redirectURL, code string) (string, error) { + ctx := context.Background() + provider, err := oidc.NewProvider(ctx, discoveryURL) + if err != nil { + return "", err + } + + oauth2Config := oauth2.Config{ + ClientID: clientID, + ClientSecret: clientSecret, + RedirectURL: redirectURL, + Endpoint: provider.Endpoint(), + Scopes: []string{oidc.ScopeOpenID}, + } + + oauth2Token, err := oauth2Config.Exchange(ctx, code) + if err != nil { + return "", err + } + + idToken, ok := oauth2Token.Extra("id_token").(string) + if !ok { + return "", fmt.Errorf("could not get id token") + } + + return fmt.Sprintf("{\"id_token\": \"%s\", \"refresh_token\": \"%s\", \"access_token\": \"%s\", \"expiry\": %d}", idToken, oauth2Token.RefreshToken, oauth2Token.AccessToken, oauth2Token.Expiry.Unix()), nil +} + +// OIDCGetAccessToken is used to retrieve an access token from a refresh token. +func OIDCGetAccessToken(discoveryURL, clientID, clientSecret, redirectURL, refreshToken string) (string, error) { + ctx := context.Background() + provider, err := oidc.NewProvider(ctx, discoveryURL) + if err != nil { + return "", err + } + + oauth2Config := oauth2.Config{ + ClientID: clientID, + ClientSecret: clientSecret, + RedirectURL: redirectURL, + Endpoint: provider.Endpoint(), + Scopes: []string{oidc.ScopeOpenID}, + } + + ts := oauth2Config.TokenSource(ctx, &oauth2.Token{RefreshToken: refreshToken}) + token, err := ts.Token() + if err != nil { + return "", err + } + + return fmt.Sprintf("{\"id_token\": \"%s\", \"refresh_token\": \"%s\", \"access_token\": \"%s\", \"expiry\": %d}", token.Extra("id_token"), token.RefreshToken, token.AccessToken, token.Expiry.Unix()), nil +} diff --git a/request/request_test.go b/request/request_test.go index ef0d629..39a18b5 100644 --- a/request/request_test.go +++ b/request/request_test.go @@ -92,3 +92,47 @@ func TestAzureGetClusters(t *testing.T) { t.Logf(data) } + +func TestOIDCGetLink(t *testing.T) { + discoveryURL := os.Getenv("OIDC_DISCOVERY_URL") + clientID := os.Getenv("OIDC_CLIENT_ID") + clientSecret := os.Getenv("OIDC_CLIENT_SECRET") + redirectURL := os.Getenv("OIDC_REDIRECT_URL") + + data, err := OIDCGetLink(discoveryURL, clientID, clientSecret, redirectURL) + if err != nil { + t.Errorf("Could not get OIDC link: %s", err.Error()) + } + + t.Logf(data) +} + +func TestOIDCGetRefreshToken(t *testing.T) { + discoveryURL := os.Getenv("OIDC_DISCOVERY_URL") + clientID := os.Getenv("OIDC_CLIENT_ID") + clientSecret := os.Getenv("OIDC_CLIENT_SECRET") + redirectURL := os.Getenv("OIDC_REDIRECT_URL") + code := os.Getenv("OIDC_CODE") + + data, err := OIDCGetRefreshToken(discoveryURL, clientID, clientSecret, redirectURL, code) + if err != nil { + t.Errorf("Could not get OIDC refresh token: %s", err.Error()) + } + + t.Logf(data) +} + +func TestOIDCGetAccessToken(t *testing.T) { + discoveryURL := os.Getenv("OIDC_DISCOVERY_URL") + clientID := os.Getenv("OIDC_CLIENT_ID") + clientSecret := os.Getenv("OIDC_CLIENT_SECRET") + redirectURL := os.Getenv("OIDC_REDIRECT_URL") + refreshToken := os.Getenv("OIDC_REFRESH_TOKEN") + + data, err := OIDCGetAccessToken(discoveryURL, clientID, clientSecret, redirectURL, refreshToken) + if err != nil { + t.Errorf("Could not get OIDC access token: %s", err.Error()) + } + + t.Logf(data) +}