mirror of
https://github.com/vmware-tanzu/pinniped.git
synced 2026-09-18 22:14:22 +00:00
Add initial "pinniped alpha login oidc" partial implementation.
Signed-off-by: Matt Moyer <moyerm@vmware.com>
This commit is contained in:
@@ -0,0 +1,45 @@
|
||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pkce
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
"golang.org/x/oauth2"
|
||||
)
|
||||
|
||||
// Generate generates a new random PKCE code.
|
||||
func Generate() (Code, error) { return generate(rand.Reader) }
|
||||
|
||||
func generate(rand io.Reader) (Code, error) {
|
||||
var buf [32]byte
|
||||
if _, err := io.ReadFull(rand, buf[:]); err != nil {
|
||||
return "", errors.WithMessage(err, "could not generate PKCE code")
|
||||
}
|
||||
return Code(hex.EncodeToString(buf[:])), nil
|
||||
}
|
||||
|
||||
// Code implements the basic options required for RFC 7636: Proof Key for Code Exchange (PKCE).
|
||||
type Code string
|
||||
|
||||
// Challenge returns the OAuth2 auth code parameter for sending the PKCE code challenge.
|
||||
func (p *Code) Challenge() oauth2.AuthCodeOption {
|
||||
b := sha256.Sum256([]byte(*p))
|
||||
return oauth2.SetAuthURLParam("code_challenge", base64.RawURLEncoding.EncodeToString(b[:]))
|
||||
}
|
||||
|
||||
// Method returns the OAuth2 auth code parameter for sending the PKCE code challenge method.
|
||||
func (p *Code) Method() oauth2.AuthCodeOption {
|
||||
return oauth2.SetAuthURLParam("code_challenge_method", "S256")
|
||||
}
|
||||
|
||||
// Verifier returns the OAuth2 auth code parameter for sending the PKCE code verifier.
|
||||
func (p *Code) Verifier() oauth2.AuthCodeOption {
|
||||
return oauth2.SetAuthURLParam("code_verifier", string(*p))
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package pkce
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/base64"
|
||||
"net/url"
|
||||
"testing"
|
||||
|
||||
"golang.org/x/oauth2"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestPKCE(t *testing.T) {
|
||||
p, err := Generate()
|
||||
require.NoError(t, err)
|
||||
|
||||
cfg := oauth2.Config{}
|
||||
authCodeURL, err := url.Parse(cfg.AuthCodeURL("", p.Challenge(), p.Method()))
|
||||
require.NoError(t, err)
|
||||
|
||||
// The code_challenge must be 256 bits (sha256) encoded as unpadded urlsafe base64.
|
||||
chal, err := base64.RawURLEncoding.DecodeString(authCodeURL.Query().Get("code_challenge"))
|
||||
require.NoError(t, err)
|
||||
require.Len(t, chal, 32)
|
||||
|
||||
// The code_challenge_method must be a fixed value.
|
||||
require.Equal(t, "S256", authCodeURL.Query().Get("code_challenge_method"))
|
||||
|
||||
// The code_verifier param should be 64 hex characters.
|
||||
verifyURL, err := url.Parse(cfg.AuthCodeURL("", p.Verifier()))
|
||||
require.NoError(t, err)
|
||||
require.Regexp(t, `\A[0-9a-f]{64}\z`, verifyURL.Query().Get("code_verifier"))
|
||||
|
||||
var empty bytes.Buffer
|
||||
p, err = generate(&empty)
|
||||
require.EqualError(t, err, "could not generate PKCE code: EOF")
|
||||
require.Empty(t, p)
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/subtle"
|
||||
"encoding/hex"
|
||||
"io"
|
||||
|
||||
"github.com/pkg/errors"
|
||||
)
|
||||
|
||||
// Generate generates a new random state parameter of an appropriate size.
|
||||
func Generate() (State, error) { return generate(rand.Reader) }
|
||||
|
||||
func generate(rand io.Reader) (State, error) {
|
||||
var buf [16]byte
|
||||
if _, err := io.ReadFull(rand, buf[:]); err != nil {
|
||||
return "", errors.WithMessage(err, "could not generate random state")
|
||||
}
|
||||
return State(hex.EncodeToString(buf[:])), nil
|
||||
}
|
||||
|
||||
// State implements some utilities for working with OAuth2 state parameters.
|
||||
type State string
|
||||
|
||||
// String returns the string encoding of this state value.
|
||||
func (s *State) String() string {
|
||||
return string(*s)
|
||||
}
|
||||
|
||||
// Validate the returned state (from a callback parameter). Returns true iff the state is valid.
|
||||
func (s *State) Valid(returnedState string) bool {
|
||||
return subtle.ConstantTimeCompare([]byte(returnedState), []byte(*s)) == 1
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
// Copyright 2020 the Pinniped contributors. All Rights Reserved.
|
||||
// SPDX-License-Identifier: Apache-2.0
|
||||
|
||||
package state
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
|
||||
"github.com/stretchr/testify/require"
|
||||
)
|
||||
|
||||
func TestState(t *testing.T) {
|
||||
s, err := Generate()
|
||||
require.NoError(t, err)
|
||||
require.Len(t, s, 32)
|
||||
require.Len(t, s.String(), 32)
|
||||
require.True(t, s.Valid(string(s)))
|
||||
require.False(t, s.Valid(string(s)+"x"))
|
||||
|
||||
var empty bytes.Buffer
|
||||
s, err = generate(&empty)
|
||||
require.EqualError(t, err, "could not generate random state: EOF")
|
||||
require.Empty(t, s)
|
||||
}
|
||||
Reference in New Issue
Block a user