assertion

package
v0.0.0-...-36f99de Latest Latest
Warning

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

Go to latest
Published: Aug 12, 2024 License: MIT Imports: 20 Imported by: 1

Documentation

Overview

package assertion authenticate OpenID Connect clients using private_key_jwt (private/public certificates instead of a shared secret), via OAuth 2.0 assertions specified in RFC 7521.

It offers better security since not secret must be transmitted (only the public certificate must be uploaded to the identity provider).

Example
package main

import (
	"crypto/rand"
	"crypto/rsa"
	"log"
	"net/http"
	"time"

	"code.pfad.fr/gopenidclient"
	"code.pfad.fr/gopenidclient/assertion"
)

// only for example purpose (to prevent circular dependency)
type concreteProvider struct {
	gopenidclient.Provider
	Issuer          string
	ClientID        string
	Scopes          []string
	ClientAssertion gopenidclient.Assertion
}

func main() {
	// persist the privateKey somewhere (can be serialized using x509.MarshalPKCS1PrivateKey for instance)
	privateKey, err := rsa.GenerateKey(rand.Reader, 2048)
	if err != nil {
		log.Fatal(err)
	}

	// create the (downgradable) certificate
	certificate := assertion.NewSesquiennial(privateKey, "Example Org")
	rs256 := assertion.RS256{
		GetThumbprint: certificate.Thumbprint,
		Validity:      time.Minute,
		CliendID:      "<OAUTH2_CLIENT_ID from identity provider>",
		Key:           privateKey,
	}

	// setup the provider using the assertion (instead of the client secret)
	// for instance using code.pfad.fr/gopenidclient/coreos.Provider
	var provider gopenidclient.Provider = (&concreteProvider{
		Issuer:          "issuer_url",
		ClientID:        rs256.CliendID,
		Scopes:          []string{"openid", "email", "profile"},
		ClientAssertion: rs256,
		// ClientSecret can be omitted
	})

	// the provider can be used like any other provider
	provider.SetRedirectURL("http://localhost:8080/auth/callback")

	// the public certificate can be exposed to ease the transmission to the identity provider
	http.HandleFunc("/auth/certificate.pem", func(w http.ResponseWriter, r *http.Request) {
		certificate.ServeDER(w, r)
	})

	// the certificate can be downgraded on ExchangeHandler.HandleCallback error
}
Output:

Index

Examples

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type RS256

type RS256 struct {
	// GetThumbprint identifies the public certificate. Implemented by [Sesquiennial.Thumbprint]
	GetThumbprint func() (Thumbprint, error)
	// Validity should be kept short (since the assertion should be used immediately by the identity provider)
	Validity time.Duration
	// ClientID identifies the client
	CliendID string
	// Key contains the private key to sign the assertion
	Key *rsa.PrivateKey
}

RS256 generates JWT assertions with RSA+SHA256. Suitable for Azure AD

func (RS256) Assert

func (j RS256) Assert(aud string) (string, error)

Assert generates, serializes and signs a JWT

func (RS256) Type

func (j RS256) Type() string

Type returns urn:ietf:params:oauth:client-assertion-type:jwt-bearer

type Sesquiennial

type Sesquiennial struct {
	CertificateSubject pkix.Name
	// duration after which a downgrade is cancelled automatically
	UpgradeAttemptAfter time.Duration
	// Return the certificate as ASN.1 marshalled X.509 v3 certificate (usually calls [x509.CreateCertificate])
	CreateCertificate func(x509.Certificate) ([]byte, error)
	// LogDowngrade will be called on every call to [Sesquiennial.Downgrade]
	LogDowngrade func(isDowngrade bool, thumbprint Thumbprint)
	// contains filtered or unexported fields
}

Sesquiennial generates public certificates valid for 18 months (always starting January 1st). By default it serves the most recent certificate. A call to Sesquiennial.Downgrade will temporarly serve the certificate from previous year (which should still be valid thanks to the 18-months period). This gives 6 months to the administrator to refresh the public certificate at the identity provider.

func NewSesquiennial

func NewSesquiennial(key *rsa.PrivateKey, organisationName string) *Sesquiennial

NewSesquiennial creates a new Sesquiennial.

func (*Sesquiennial) DER

func (cg *Sesquiennial) DER(year int) ([]byte, error)

DER returns a ASN.1 marshalled X.509 v3 certificate, valid for 18 months, starting on {year}-01-01.

func (*Sesquiennial) Downgrade

func (cg *Sesquiennial) Downgrade() bool

Downgrade will serve the previous certificate if not downgraded yet (and return true). Calling it again will cancel the downgrade and return false. This should be called in case the Identity Provider denied a request with a "Client assertion failed signature validation" error.

func (*Sesquiennial) IsDowngraded

func (cg *Sesquiennial) IsDowngraded() bool

IsDowngraded indicates if the previous certificate is being used. I can be used to display a message to the administrator to refresh the public certificate at the identity provider.

func (*Sesquiennial) ServeDER

func (cg *Sesquiennial) ServeDER(w http.ResponseWriter, r *http.Request) error

ServeDER will serve the current public certificate as DER. You can get the previous version, by adding a "?old" query parameter

func (*Sesquiennial) Thumbprint

func (cg *Sesquiennial) Thumbprint() (Thumbprint, error)

Thumbprint returns the thumbprint of the currently served certificate. It is suitable to use for the GetThumbprint field of RS256.

type Thumbprint

type Thumbprint struct {
	Sha1   string `json:"x5t,omitempty"`      // x5t according to https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.7
	Sha256 string `json:"x5t#S256,omitempty"` // x5t#S256 according to https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.8
}

Thumbprint contains the base64 encoded-hashes of a given certificate.

func NewThumbprint

func NewThumbprint(der []byte) (tb Thumbprint)

NewThumbprint returns the base64 encoded-hashes of the provided DER certificate.

Jump to

Keyboard shortcuts

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