githubadapter

package
v0.0.4 Latest Latest
Warning

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

Go to latest
Published: Aug 24, 2026 License: Apache-2.0 Imports: 15 Imported by: 0

Documentation

Overview

Package githubadapter provides a hardened GitHub implementation of the credbound.SSOProvider port. GitHub is not an OpenID Connect issuer — it speaks plain OAuth 2.0 and identity comes from its REST API — so the generic OIDC adapter (ssoadapter) cannot serve it; this package owns that protocol gap with the standard library only.

Responsibilities

The adapter owns the protocol exchange only: the authorization redirect (with a random state, a PKCE S256 challenge, and allow_signup=false), the code exchange at GitHub's token endpoint (presenting the PKCE verifier), and the REST reads of GET /user and GET /user/emails. Credbound keeps everything else — the sealed continuation carrying the adapter's opaque session, ceremony TTL, identity linking, persistence, audit, and revocation.

Registration

Register a provider by wiring it into credbound.Config.SSOProviders:

provider, err := githubadapter.New(githubadapter.Config{
	ConfigurationID: "0198b463-51a2-7cde-8000-0123456789ab", // UUIDv7 chosen by the host
	ClientID:        os.Getenv("GITHUB_CLIENT_ID"),
	ClientSecret:    os.Getenv("GITHUB_CLIENT_SECRET"),
	RedirectURL:     "https://app.example.com/sso/callback",
})
if err != nil {
	log.Fatal(err)
}
manager, err := credbound.New(credbound.Config{
	Store:        store,
	Passwords:    hasher,
	SecretKey:    secretKey,
	SSOProviders: []credbound.SSOProvider{provider},
})

The provider's kind is always credbound.SSOProviderGitHub.

Callback handling

The host's HTTP callback handler forwards the provider response verbatim to credbound's FinishSSO. The adapter accepts the full callback URL, the bare query string, or a JSON object with "code" and "state" fields:

func callback(w http.ResponseWriter, r *http.Request) {
	continuation := readContinuationCookie(r)
	auth, err := manager.FinishSSO(r.Context(), continuation, []byte(r.URL.String()))
	// ...
}

Subject and email trust

The credbound subject is the decimal form of GitHub's numeric account id — the only stable identifier GitHub offers. The login is never used: logins are renameable and can be re-registered by someone else, which would let a freed name inherit a victim's linked identity. The primary email from GET /user/emails is forwarded with GitHub's own verified flag; when the user:email scope was declined the adapter falls back to the public profile email as unverified. Credbound keys SSO identities on issuer and subject, never on email, so an absent or unverified address only limits convenience.

No step-up, AAL1 only

GitHub's authorization endpoint has no prompt=login or max_age equivalent: an authorization silently reuses whatever browser session GitHub already holds, and nothing in the response proves a fresh login. Begin therefore fails with ErrStepUpUnsupported when SSORequest.ForceReauthentication is set — a step-up ceremony must not pretend a re-authentication happened. For the same reason the adapter asserts no ACR or AMR, so without an explicit credbound.Config.SSOAssurance opt-out (TrustUnverified) a GitHub sign-in grants only AAL1. That is the correct posture: the adapter cannot verify that GitHub enforced a second factor. Hosts with hard step-up or MFA-assurance requirements should pair GitHub sign-in with a credbound second factor, or prefer an OIDC provider (ssoadapter) for those flows.

Index

Constants

This section is empty.

Variables

View Source
var (
	// ErrStateMismatch reports that the state parameter returned by GitHub
	// does not match the one issued in Begin.
	ErrStateMismatch = errors.New("githubadapter: state parameter mismatch")
	// ErrStepUpUnsupported reports a Begin with ForceReauthentication set:
	// GitHub's authorization endpoint cannot force a fresh login, so the
	// adapter refuses to run a step-up ceremony it cannot honor rather than
	// silently reusing GitHub's existing browser session.
	ErrStepUpUnsupported = errors.New("githubadapter: github cannot force re-authentication; step-up ceremonies must use another provider")
)

Sentinel errors for the failures hosts most often want to distinguish in logs. Credbound maps every Begin/Finish error to ErrInvalidCredentials before it reaches the end user.

Functions

This section is empty.

Types

type Config

type Config struct {
	// ConfigurationID is the host-chosen UUIDv7 under which credbound
	// indexes this provider and its linked identities. Required.
	ConfigurationID credbound.UUID
	// ClientID is the OAuth app's client identifier. Required.
	ClientID string
	// ClientSecret authenticates the client at the token endpoint.
	// Required: GitHub OAuth apps are confidential clients.
	ClientSecret string
	// RedirectURL is the callback URL registered at GitHub. Required.
	// HTTPS is mandatory except for loopback hosts, which may use HTTP for
	// local development and tests.
	RedirectURL string
	// Scopes defaults to "read:user user:email" — enough to read the stable
	// numeric account id and the primary email with its verified flag.
	Scopes []string
	// HTTPClient is used for the code exchange and the REST calls. Defaults
	// to a client with a 10 second timeout; a supplied client without a
	// timeout is shallow-copied and given the default.
	HTTPClient *http.Client
	// AuthorizeURL overrides GitHub's authorization endpoint, for tests.
	// Defaults to https://github.com/login/oauth/authorize.
	AuthorizeURL string
	// TokenURL overrides GitHub's token endpoint, for tests. Defaults to
	// https://github.com/login/oauth/access_token.
	TokenURL string
	// APIBaseURL overrides the GitHub REST API base, for tests. Defaults to
	// https://api.github.com.
	APIBaseURL string
}

Config describes one GitHub OAuth app registration.

type Provider

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

Provider is the GitHub implementation of credbound.SSOProvider. It is stateless across ceremonies: everything a Finish needs travels inside the opaque Session bytes that credbound seals into its continuation.

func New

func New(config Config) (*Provider, error)

New validates the configuration and returns a Provider ready to register in credbound.Config.SSOProviders. Construction performs no network calls.

func (*Provider) Begin

Begin implements credbound.SSOProvider. It generates a fresh state and a PKCE S256 challenge, builds the GitHub authorization URL (with allow_signup=false so the ceremony only authenticates existing GitHub accounts), and returns the state and verifier as opaque Session bytes for credbound to seal into its continuation.

A request with ForceReauthentication set fails with ErrStepUpUnsupported: GitHub's authorization endpoint has no prompt/max_age equivalent, so the adapter cannot make GitHub re-verify the user, and a step-up ceremony must not pretend it did.

func (*Provider) ConfigurationID

func (p *Provider) ConfigurationID() credbound.UUID

ConfigurationID implements credbound.SSOProvider.

func (*Provider) Finish

func (p *Provider) Finish(ctx context.Context, sessionBytes, response []byte) (credbound.SSOClaims, error)

Finish implements credbound.SSOProvider. sessionBytes is the Session issued by Begin (returned by credbound from its sealed continuation) and response is the raw callback payload from the host: the full callback URL, the bare query string, or a JSON object with code and state fields.

Finish verifies the state with a constant-time comparison, exchanges the code at the token endpoint, then reads the account through the REST API: the numeric account id becomes the subject (logins are renameable and never used) and the primary email is forwarded with GitHub's own verified flag. Replay of a callback is bounded by credbound's continuation TTL and by GitHub's single-use authorization code; the adapter itself keeps no state.

func (*Provider) Kind

Kind implements credbound.SSOProvider.

Jump to

Keyboard shortcuts

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