Refactor and rename ./internal/oidcclient/login to ./internal/oidcclient.
This commit is contained in:
parent
4ef41f969d
commit
7f6a82aa91
@ -10,15 +10,15 @@ import (
|
|||||||
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
|
clientauthenticationv1beta1 "k8s.io/client-go/pkg/apis/clientauthentication/v1beta1"
|
||||||
|
|
||||||
"go.pinniped.dev/internal/oidcclient/login"
|
"go.pinniped.dev/internal/oidcclient"
|
||||||
)
|
)
|
||||||
|
|
||||||
//nolint: gochecknoinits
|
//nolint: gochecknoinits
|
||||||
func init() {
|
func init() {
|
||||||
loginCmd.AddCommand(oidcLoginCommand(login.Run))
|
loginCmd.AddCommand(oidcLoginCommand(oidcclient.Login))
|
||||||
}
|
}
|
||||||
|
|
||||||
func oidcLoginCommand(loginFunc func(issuer string, clientID string, opts ...login.Option) (*login.Token, error)) *cobra.Command {
|
func oidcLoginCommand(loginFunc func(issuer string, clientID string, opts ...oidcclient.Option) (*oidcclient.Token, error)) *cobra.Command {
|
||||||
var (
|
var (
|
||||||
cmd = cobra.Command{
|
cmd = cobra.Command{
|
||||||
Args: cobra.NoArgs,
|
Args: cobra.NoArgs,
|
||||||
@ -40,18 +40,18 @@ func oidcLoginCommand(loginFunc func(issuer string, clientID string, opts ...log
|
|||||||
mustMarkRequired(&cmd, "issuer", "client-id")
|
mustMarkRequired(&cmd, "issuer", "client-id")
|
||||||
|
|
||||||
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
cmd.RunE = func(cmd *cobra.Command, args []string) error {
|
||||||
opts := []login.Option{
|
opts := []oidcclient.Option{
|
||||||
login.WithContext(cmd.Context()),
|
oidcclient.WithContext(cmd.Context()),
|
||||||
login.WithScopes(scopes),
|
oidcclient.WithScopes(scopes),
|
||||||
}
|
}
|
||||||
|
|
||||||
if listenPort != 0 {
|
if listenPort != 0 {
|
||||||
opts = append(opts, login.WithListenPort(listenPort))
|
opts = append(opts, oidcclient.WithListenPort(listenPort))
|
||||||
}
|
}
|
||||||
|
|
||||||
// --skip-browser replaces the default "browser open" function with one that prints to stderr.
|
// --skip-browser replaces the default "browser open" function with one that prints to stderr.
|
||||||
if skipBrowser {
|
if skipBrowser {
|
||||||
opts = append(opts, login.WithBrowserOpen(func(url string) error {
|
opts = append(opts, oidcclient.WithBrowserOpen(func(url string) error {
|
||||||
cmd.PrintErr("Please log in: ", url, "\n")
|
cmd.PrintErr("Please log in: ", url, "\n")
|
||||||
return nil
|
return nil
|
||||||
}))
|
}))
|
||||||
@ -69,8 +69,8 @@ func oidcLoginCommand(loginFunc func(issuer string, clientID string, opts ...log
|
|||||||
APIVersion: "client.authentication.k8s.io/v1beta1",
|
APIVersion: "client.authentication.k8s.io/v1beta1",
|
||||||
},
|
},
|
||||||
Status: &clientauthenticationv1beta1.ExecCredentialStatus{
|
Status: &clientauthenticationv1beta1.ExecCredentialStatus{
|
||||||
ExpirationTimestamp: &metav1.Time{Time: tok.IDTokenExpiry},
|
ExpirationTimestamp: &tok.IDToken.Expiry,
|
||||||
Token: tok.IDToken,
|
Token: tok.IDToken.Token,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -9,9 +9,10 @@ import (
|
|||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"go.pinniped.dev/internal/here"
|
"go.pinniped.dev/internal/here"
|
||||||
"go.pinniped.dev/internal/oidcclient/login"
|
"go.pinniped.dev/internal/oidcclient"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestLoginOIDCCommand(t *testing.T) {
|
func TestLoginOIDCCommand(t *testing.T) {
|
||||||
@ -87,13 +88,18 @@ func TestLoginOIDCCommand(t *testing.T) {
|
|||||||
var (
|
var (
|
||||||
gotIssuer string
|
gotIssuer string
|
||||||
gotClientID string
|
gotClientID string
|
||||||
gotOptions []login.Option
|
gotOptions []oidcclient.Option
|
||||||
)
|
)
|
||||||
cmd := oidcLoginCommand(func(issuer string, clientID string, opts ...login.Option) (*login.Token, error) {
|
cmd := oidcLoginCommand(func(issuer string, clientID string, opts ...oidcclient.Option) (*oidcclient.Token, error) {
|
||||||
gotIssuer = issuer
|
gotIssuer = issuer
|
||||||
gotClientID = clientID
|
gotClientID = clientID
|
||||||
gotOptions = opts
|
gotOptions = opts
|
||||||
return &login.Token{IDToken: "test-id-token", IDTokenExpiry: time1}, nil
|
return &oidcclient.Token{
|
||||||
|
IDToken: &oidcclient.IDToken{
|
||||||
|
Token: "test-id-token",
|
||||||
|
Expiry: metav1.NewTime(time1),
|
||||||
|
},
|
||||||
|
}, nil
|
||||||
})
|
})
|
||||||
require.NotNil(t, cmd)
|
require.NotNil(t, cmd)
|
||||||
|
|
||||||
|
@ -1,8 +1,8 @@
|
|||||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
// Package login implements a CLI OIDC login flow.
|
// Package oidcclient implements a CLI OIDC login flow.
|
||||||
package login
|
package oidcclient
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -15,6 +15,7 @@ import (
|
|||||||
"github.com/coreos/go-oidc"
|
"github.com/coreos/go-oidc"
|
||||||
"github.com/pkg/browser"
|
"github.com/pkg/browser"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"go.pinniped.dev/internal/httputil/httperr"
|
"go.pinniped.dev/internal/httputil/httperr"
|
||||||
"go.pinniped.dev/internal/httputil/securityheader"
|
"go.pinniped.dev/internal/httputil/securityheader"
|
||||||
@ -55,13 +56,7 @@ type callbackResult struct {
|
|||||||
err error
|
err error
|
||||||
}
|
}
|
||||||
|
|
||||||
type Token struct {
|
// Option is an optional configuration for Login().
|
||||||
*oauth2.Token
|
|
||||||
IDToken string `json:"id_token"`
|
|
||||||
IDTokenExpiry time.Time `json:"id_token_expiry"`
|
|
||||||
}
|
|
||||||
|
|
||||||
// Option is an optional configuration for Run().
|
|
||||||
type Option func(*handlerState) error
|
type Option func(*handlerState) error
|
||||||
|
|
||||||
// WithContext specifies a specific context.Context under which to perform the login. If this option is not specified,
|
// WithContext specifies a specific context.Context under which to perform the login. If this option is not specified,
|
||||||
@ -105,8 +100,8 @@ func WithBrowserOpen(openURL func(url string) error) Option {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Run an OAuth2/OIDC authorization code login using a localhost listener.
|
// Login performs an OAuth2/OIDC authorization code login using a localhost listener.
|
||||||
func Run(issuer string, clientID string, opts ...Option) (*Token, error) {
|
func Login(issuer string, clientID string, opts ...Option) (*Token, error) {
|
||||||
h := handlerState{
|
h := handlerState{
|
||||||
issuer: issuer,
|
issuer: issuer,
|
||||||
clientID: clientID,
|
clientID: clientID,
|
||||||
@ -262,9 +257,18 @@ func (h *handlerState) handleAuthCodeCallback(w http.ResponseWriter, r *http.Req
|
|||||||
}
|
}
|
||||||
|
|
||||||
h.callbacks <- callbackResult{token: &Token{
|
h.callbacks <- callbackResult{token: &Token{
|
||||||
Token: oauth2Tok,
|
AccessToken: &AccessToken{
|
||||||
IDToken: idTok,
|
Token: oauth2Tok.AccessToken,
|
||||||
IDTokenExpiry: validated.Expiry,
|
Type: oauth2Tok.TokenType,
|
||||||
|
Expiry: metav1.NewTime(oauth2Tok.Expiry),
|
||||||
|
},
|
||||||
|
RefreshToken: &RefreshToken{
|
||||||
|
Token: oauth2Tok.RefreshToken,
|
||||||
|
},
|
||||||
|
IDToken: &IDToken{
|
||||||
|
Token: idTok,
|
||||||
|
Expiry: metav1.NewTime(validated.Expiry),
|
||||||
|
},
|
||||||
}}
|
}}
|
||||||
_, _ = w.Write([]byte("you have been logged in and may now close this tab"))
|
_, _ = w.Write([]byte("you have been logged in and may now close this tab"))
|
||||||
return nil
|
return nil
|
@ -1,7 +1,7 @@
|
|||||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||||
// SPDX-License-Identifier: Apache-2.0
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
package login
|
package oidcclient
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"context"
|
"context"
|
||||||
@ -18,6 +18,7 @@ import (
|
|||||||
"github.com/stretchr/testify/require"
|
"github.com/stretchr/testify/require"
|
||||||
"golang.org/x/oauth2"
|
"golang.org/x/oauth2"
|
||||||
"gopkg.in/square/go-jose.v2"
|
"gopkg.in/square/go-jose.v2"
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
|
||||||
"go.pinniped.dev/internal/httputil/httperr"
|
"go.pinniped.dev/internal/httputil/httperr"
|
||||||
"go.pinniped.dev/internal/mocks/mockkeyset"
|
"go.pinniped.dev/internal/mocks/mockkeyset"
|
||||||
@ -26,18 +27,21 @@ import (
|
|||||||
"go.pinniped.dev/internal/oidcclient/state"
|
"go.pinniped.dev/internal/oidcclient/state"
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestRun(t *testing.T) {
|
func TestLogin(t *testing.T) {
|
||||||
time1 := time.Date(3020, 10, 12, 13, 14, 15, 16, time.UTC)
|
time1 := time.Date(3020, 10, 12, 13, 14, 15, 16, time.UTC)
|
||||||
testToken := Token{
|
testToken := Token{
|
||||||
Token: &oauth2.Token{
|
AccessToken: &AccessToken{
|
||||||
AccessToken: "test-access-token",
|
Token: "test-access-token",
|
||||||
RefreshToken: "test-refresh-token",
|
Expiry: metav1.NewTime(time1.Add(1 * time.Minute)),
|
||||||
Expiry: time1.Add(1 * time.Minute),
|
},
|
||||||
|
RefreshToken: &RefreshToken{
|
||||||
|
Token: "test-refresh-token",
|
||||||
|
},
|
||||||
|
IDToken: &IDToken{
|
||||||
|
Token: "test-id-token",
|
||||||
|
Expiry: metav1.NewTime(time1.Add(2 * time.Minute)),
|
||||||
},
|
},
|
||||||
IDToken: "test-id-token",
|
|
||||||
IDTokenExpiry: time1.Add(2 * time.Minute),
|
|
||||||
}
|
}
|
||||||
_ = testToken
|
|
||||||
|
|
||||||
// Start a test server that returns 500 errors
|
// Start a test server that returns 500 errors
|
||||||
errorServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
errorServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||||
@ -223,7 +227,7 @@ func TestRun(t *testing.T) {
|
|||||||
for _, tt := range tests {
|
for _, tt := range tests {
|
||||||
tt := tt
|
tt := tt
|
||||||
t.Run(tt.name, func(t *testing.T) {
|
t.Run(tt.name, func(t *testing.T) {
|
||||||
tok, err := Run(tt.issuer, tt.clientID,
|
tok, err := Login(tt.issuer, tt.clientID,
|
||||||
WithContext(context.Background()),
|
WithContext(context.Background()),
|
||||||
WithListenPort(0),
|
WithListenPort(0),
|
||||||
WithScopes([]string{"test-scope"}),
|
WithScopes([]string{"test-scope"}),
|
||||||
@ -393,7 +397,7 @@ func TestHandleAuthCodeCallback(t *testing.T) {
|
|||||||
}
|
}
|
||||||
require.NoError(t, result.err)
|
require.NoError(t, result.err)
|
||||||
require.NotNil(t, result.token)
|
require.NotNil(t, result.token)
|
||||||
require.Equal(t, result.token.IDToken, tt.returnIDTok)
|
require.Equal(t, result.token.IDToken.Token, tt.returnIDTok)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
49
internal/oidcclient/types.go
Normal file
49
internal/oidcclient/types.go
Normal file
@ -0,0 +1,49 @@
|
|||||||
|
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||||
|
// SPDX-License-Identifier: Apache-2.0
|
||||||
|
|
||||||
|
package oidcclient
|
||||||
|
|
||||||
|
import (
|
||||||
|
metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
|
||||||
|
)
|
||||||
|
|
||||||
|
// AccessToken is an OAuth2 access token.
|
||||||
|
type AccessToken struct {
|
||||||
|
// Token is the token that authorizes and authenticates the requests.
|
||||||
|
Token string `json:"token"`
|
||||||
|
|
||||||
|
// Type is the type of token.
|
||||||
|
Type string `json:"type,omitempty"`
|
||||||
|
|
||||||
|
// Expiry is the optional expiration time of the access token.
|
||||||
|
Expiry metav1.Time `json:"expiryTimestamp,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// RefreshToken is an OAuth2 refresh token.
|
||||||
|
type RefreshToken struct {
|
||||||
|
// Token is a token that's used by the application (as opposed to the user) to refresh the access token if it expires.
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// IDToken is an OpenID Connect ID token.
|
||||||
|
type IDToken struct {
|
||||||
|
// Token is an OpenID Connect ID token.
|
||||||
|
Token string `json:"token"`
|
||||||
|
|
||||||
|
// Expiry is the optional expiration time of the ID token.
|
||||||
|
Expiry metav1.Time `json:"expiryTimestamp,omitempty"`
|
||||||
|
}
|
||||||
|
|
||||||
|
// Token contains the elements of an OIDC session.
|
||||||
|
type Token struct {
|
||||||
|
// AccessToken is the token that authorizes and authenticates the requests.
|
||||||
|
AccessToken *AccessToken `json:"access,omitempty"`
|
||||||
|
|
||||||
|
// RefreshToken is a token that's used by the application
|
||||||
|
// (as opposed to the user) to refresh the access token
|
||||||
|
// if it expires.
|
||||||
|
RefreshToken *RefreshToken `json:"refresh,omitempty"`
|
||||||
|
|
||||||
|
// IDToken is an OpenID Connect ID token.
|
||||||
|
IDToken *IDToken `json:"id,omitempty"`
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user