pkce

package module
v0.1.2 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jan 26, 2022 License: MIT Imports: 6 Imported by: 2

README

pkce

Go Reference Go Report Card Go Test golangci-lint

pkce implements the client side of RFC 7636 "Proof Key for Code Exchange by OAuth Public Clients" (PKCE) to enable the generation of cryptographically secure and specification compliant code verifiers and code challenges. With ✨ no external dependencies ✨.

Getting Started

pkce makes use of go mod, you can install it by using go get:

go get github.com/matthewhartstonge/pkce

Examples

Structs

For those that like abstractions, feel free to enjoy "safety™":

package main

import (
	"fmt"

	"github.com/matthewhartstonge/pkce"
)

func main() {
	// Generate a secure proof key! 
	// Make sure yo do this each time you want a new proof key - it's stateful.
	key, err := pkce.New()
	if err != nil {
		panic(err)
	}

	fmt.Println("my generated code verifier is:", key.CodeVerifier())
	fmt.Println("my generated plain code challenge is:", key.CodeChallenge())

	// Finally - on the server-side side, we can verify the received code 
	// verifier:
	receivedCodeVerifier := "#yolo-cant-verify-me-mr-mcbaggins"
	isValid := key.VerifyCodeVerifier(receivedCodeVerifier)
	fmt.Println("is the received code verifier valid?", isValid)
}

Okay, so that was a bit easy... But, what can I configure?!?

package main

import (
	"fmt"

	"github.com/matthewhartstonge/pkce"
)

func main() {
	// Generate a ... proof key!
	key, err := pkce.New(
		// pkce.WithCodeVerifierLength enables increasing entropy for 
		// super-duper securities!
		pkce.WithCodeVerifierLength(9001),

		// pkce.WithChallengeMethod enables setting the PKCE mode, which is 
		// really code name for setting the method to "plain" for, you know, if
		// you've got a non-compliant OAuth PKCE accepting server that may 
		// require backwards compatibility. #SnarkIntended
		pkce.WithChallengeMethod(pkce.Plain),

		// pkce.WithCodeVerifier enables BYO code verifier.
		//
		// ... I hope you use a secure implementation ...
		//
		// This is mainly useful if you like the struct style of encapsulation, 
		// or if loading the verifier from a datastore.
		//
		// Using this option will disable code verifier generation, therefore 
		// `pkce.WithCodeVerifierLength` will be redundant if specified.
		pkce.WithCodeVerifier([]byte("#YOLO")),
	)
	if err != nil {
		// hah, yeah, there's gonna be an error or two...
		panic(err)
	}

	// ... otherwise, it's business as usual ...
Functional

For those that like functions, you can fight against your own "to err == programmer"

package main

import (
	"fmt"

	"github.com/matthewhartstonge/pkce"
)

func main() {
	// Generate a secure code verifier!
	codeVerifier, err := pkce.GenerateCodeVerifier(50)
	if err != nil {
		panic(err)
	}

	// OR, lawd forbid, you can generate and send in your own code verifier...
	//    ... don't do this ...

	// Then we can generate a code challenge based on the incoming code 
	// challenge method
	codeChallenge, err := pkce.GenerateCodeChallenge(pkce.S256, codeVerifier)
	if err != nil {
		panic(err)
	}

	fmt.Println("my manually generated code verifier is:", codeVerifier)
	fmt.Println("my manually generated code challenge is:", codeChallenge)

	// Finally - on the server-side side, we can verify the received code 
	// verifier:
	incomingCodeVerifier := "#yolo-cant-verify-me-mr-mcbaggins"
	isValid := pkce.VerifyCodeVerifier(pkce.S256, incomingCodeVerifier, codeChallenge)
	fmt.Println("is the received code verifier valid?", isValid)
}

What is PKCE?

Great Question!

For more information on "Proof Key for Code Exchange (PKCE) by OAuth Public Clients" (or for some light bedtime reading) check out the following links:

Documentation

Overview

Package pkce implements proof key generation defined by RFC 7636 to enable generation and validation of code verifiers and code challenges.

"Proof Key for Code Exchange" (PKCE, pronounced "pixy") was created as a technique to mitigate against the authorization code interception attack.

For a detailed specification of PKCE (RFC 7636) see [1].

Terminology:

  1. code verifier A cryptographically random string that is used to correlate the authorization request to the token request.

  2. code challenge A challenge derived from the code verifier that is sent in the authorization request, to be verified against later.

  3. code challenge method A method that was used to derive code challenge.

  4. Base64url Encoding Base64 encoding using the URL- and filename-safe character set defined in Section 5 of [RFC4648], with all trailing '=' characters omitted (as permitted by Section 3.2 of [RFC4648]) and without the inclusion of any line breaks, whitespace, or other additional characters. (See Appendix A for notes on implementing base64url encoding without padding.)

[1] https://datatracker.ietf.org/doc/html/rfc7636

Index

Constants

View Source
const (
	// ParamCodeChallenge (required) provides the url query param key required
	// to send a PKCE code challenge as part of the Authorization Request.
	ParamCodeChallenge = "code_challenge"

	// ParamCodeChallengeMethod (optional) provides the url query param key
	// required to send the PKCE code challenge method as part of the
	// Authorization Request. Defaults to "plain" if not present in the request.
	ParamCodeChallengeMethod = "code_challenge_method"

	// ParamCodeVerifier provides the url query param key required to send a
	// PKCE code verifier as part of the token request.
	ParamCodeVerifier = "code_verifier"
)

Variables

View Source
var (
	// ErrMethodDowngrade enforces compliance with RFC 7636, 7.2.
	//
	// Clients MUST NOT downgrade to "plain" after trying the "S256" method.
	// Servers that support PKCE are required to support "S256", and servers
	// that do not support PKCE will simply ignore the unknown
	// "code_verifier".  Because of this, an error when "S256" is presented
	// can only mean that the server is faulty or that a MITM attacker is
	// trying a downgrade attack.
	ErrMethodDowngrade = errors.New("clients must not downgrade to 'plain' after trying the 'S256' method")

	// ErrMethodNotSupported enforces the use of compliant transform methods
	ErrMethodNotSupported = errors.New("clients must use either 'plain' or 'S256' as a transform method")

	// ErrVerifierCharacters enforces character compliance with the unreserved
	// character set as specified in RFC 7636, 4.1.
	ErrVerifierCharacters = fmt.Errorf(
		"code verifier must only contain unreserved characters from the set: {'%s'}",
		unreserved,
	)

	// ErrVerifierLength enforces compliance with the minimum and maximum
	// lengths as specified in RFC 7636, 4.1.
	ErrVerifierLength = fmt.Errorf(
		"code verifier must be between %d and %d characters long",
		verifierMinLen,
		verifierMaxLen,
	)
)

Functions

func GenerateCodeChallenge

func GenerateCodeChallenge(method Method, codeVerifier string) (out string, err error)

GenerateCodeChallenge takes a code verifier and method to generate a code challenge.

func GenerateCodeVerifier

func GenerateCodeVerifier(n int) (string, error)

GenerateCodeVerifier generates an RFC7636 compliant, cryptographically secure code verifier.

func VerifyCodeVerifier

func VerifyCodeVerifier(method Method, codeVerifier string, codeChallenge string) bool

VerifyCodeVerifier enables servers to verify the received code verifier.

Types

type Key

type Key struct {
	// contains filtered or unexported fields
}

Key provides the proof key for secure code exchange.

func New

func New(opts ...Option) (key *Key, err error)

New returns a Proof Key

func (*Key) ChallengeMethod

func (k *Key) ChallengeMethod() Method

ChallengeMethod returns the configured key's method for generating a code challenge.

func (*Key) CodeChallenge

func (k *Key) CodeChallenge() string

CodeChallenge returns the challenge for the configured code verifier. Will generate a verifier if nil.

func (*Key) CodeVerifier

func (k *Key) CodeVerifier() string

CodeVerifier returns the code verifier.

func (*Key) SetChallengeMethod

func (k *Key) SetChallengeMethod(method Method) error

SetChallengeMethod enables upgrading code challenge generation method.

func (*Key) VerifyCodeVerifier

func (k *Key) VerifyCodeVerifier(codeVerifier string) bool

VerifyCodeVerifier provides a convenience function, for if you've loaded the code verifier into the key. If not, this won't really be useful to use...

type Method

type Method string

Method specifies the code challenge transformation method that was used to derive the code challenge.

const (
	// Plain method specifies that the code challenge has had no transformation
	// performed on the code verifier.
	//
	// code_challenge = code_verifier
	//
	// The plain transformation is for compatibility with existing
	// deployments and for constrained environments that can't use the S256
	// transformation.
	//
	Plain Method = "plain"

	// S256 method specifies that the code challenge has been transformed by
	// being hashed by SHA-256 then base64url-encoded.
	//
	// code_challenge = BASE64URL-ENCODE(SHA256(ASCII(code_verifier)))
	//
	// If the client is capable of using "S256", it MUST use "S256", as
	// "S256" is Mandatory To Implement (MTI) on the server.  Clients are
	// permitted to use "plain" only if they cannot support "S256" for some
	// technical reason and know via out-of-band configuration that the
	// server supports "plain".
	S256 Method = "S256"
)

func (Method) String

func (m Method) String() string

String implements Stringer.

type Option

type Option func(*Key) error

Option enables variadic PKCE Key options to be configured.

func WithChallengeMethod

func WithChallengeMethod(method Method) Option

WithChallengeMethod enables specifying the challenge transformation method. Should only be used to downgrade to plain if required.

func WithCodeVerifier

func WithCodeVerifier(codeVerifier []byte) Option

WithCodeVerifier enables supplying your own code verifier. Disables code verifier generation.

func WithCodeVerifierLength

func WithCodeVerifierLength(n int) Option

WithCodeVerifierLength enables specifying the length of the code verifier to be generated.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL