oidclogin

package module
v0.2.1 Latest Latest
Warning

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

Go to latest
Published: Sep 23, 2023 License: MIT Imports: 14 Imported by: 2

README

OIDC login

license

Welcome to OIDC Login, a simple and secure way to authorize your application with the OpenID Connect (OIDC) protocol. OIDC is supported by most major platforms, including Okta, Google, Auth0, Keycloak, Authentik, and others.

OpenID Connect (OIDC) is a simple identity layer on top of the OAuth 2.0 protocol that allows clients to verify the identity of the end-user based on the authentication performed by an authorization server. OIDC provides a standard way for clients to authenticate users, and obtain basic user profile information.

The library supports both Client Credentials (M2M) and Authorization Code flow (UI).

Motivation

While there are several alternatives available, such as goth, authboss, and auth, they all have similar flaws, including global state, being very opinionated, and having so-so support for OIDC.

At OIDC Login, we follow the UNIX-like idea of doing one thing, but doing it well. Our code is focused on being auditable, maintainable, and flexible as much as possible.

Usage

Checkout Go docs and examples.

To use OIDC Login, simply follow the code below:

package main

import (
	"context"
	"net/http"

	"github.com/reddec/oidc-login"
)

func main() {
	auth, err := oidclogin.New(context.Background(), oidclogin.Config{
		IssuerURL:    "https://some-idp.example.com",
		ClientID:     "<MY CLIENT ID>",
		ClientSecret: "<MY SECRET>",
	})
	if err != nil {
		panic(err) // handle it properly in production
	}

	// add secured route (or group)
	http.Handle("/", auth.SecureFunc(func(writer http.ResponseWriter, request *http.Request) {
		token := oidclogin.Token(request)
		name := oidclogin.User(token)
		writer.Header().Set("Content-Type", "text/html")
		_, _ = writer.Write([]byte("<html><body><h1>Hello, " + name + "</h1></body></html>"))
	}))

	// add callback prefixes
	http.Handle(oidclogin.Prefix, auth)
	// ...
}



Notes to Admins

Here are some notes for administrators to keep in mind while using OIDC Login:

  • Set the public server URL in case you cannot control X-Forwarded-Host and X-Forwarded-Proto headers by reverse proxy.
  • Set persistent storage for sessions.
  • It is highly recommended to secure your application by OWASP recommended headers. Here is some code you can use to set these headers:
func SetOWASPHeaders(writer http.ResponseWriter) {
  writer.Header().Set("X-Frame-Options", "DENY") // helps with click hijacking
  writer.Header().Set("X-XSS-Protection", "1")
  writer.Header().Set("X-Content-Type-Options", "nosniff") // helps with content-type substitution
  writer.Header().Set("Referrer-Policy", "strict-origin-when-cross-origin") // disables cross-origin requests 
}

Documentation

Index

Constants

View Source
const Prefix string = "/oauth2/" // default prefix for callback and logout. Can be changed by [Config.CallbackPrefix].

Variables

This section is empty.

Functions

func Email

func Email(token *oidc.IDToken) string

Email from claims. May return empty string.

func Groups

func Groups(token *oidc.IDToken) []string

Groups from claims. May return nil slice.

func Token

func Token(req *http.Request) *oidc.IDToken

Token gets token from current session. If handler is not from OIDC.Secure then the value can be nil or invalid.

func User

func User(token *oidc.IDToken) string

User based on (in order of priority) claims: preferred_username, email, subject.

Types

type Config

type Config struct {
	// OIDC URL (ex: https://example.com/realm/my-realm).
	IssuerURL string
	// OIDC client name. This value doesn't need to be super secret.
	// Not advisable to share, but not designed to be a secret.
	ClientID string
	// OIDC client secret (aka: confidential mode)
	ClientSecret string
	// (optional) list of OAuth scopes. Default is minimal required: openid - [oidc.ScopeOpenID]
	Scopes []string
	// (optional) public server URL,
	// if not set system will try to detect it by request URL, X-Forwarded-Host, and X-Forwarded-Proto which is
	// potentially is not secure and can be forged (unless there is secure forward proxy in front)
	ServerURL string
	// (optional) prefix for path for callbacks URL. Default prefix is [Prefix]
	CallbackPrefix string
	// (optional) session manager. If not set, default in-memory session manager will be used.
	SessionManager *scs.SessionManager
	// (optional) handle user post-authorization.
	// If handler returned any error, user will be rejected with 403 code, otherwise it will return 303	StatusSeeOther.
	// Callback may set destination URL via Location header; if header is not set, root server URL will be used.
	// The callback always called for each header-based request (since it's stateless).
	// It's good place for claims-based filtering or sessions.
	PostAuth func(writer http.ResponseWriter, req *http.Request, idToken *oidc.IDToken) error
	// (optional) handle before user authorization (redirect to OIDC portal). The callback will not be called for M2M.
	// If handler returned any error, request will be rejected with 403 code.
	// It's a good place to save current URL in order to redirect user after authorization to the initial page.
	BeforeAuth func(writer http.ResponseWriter, req *http.Request) error
	// (optional) handle situation after ID token refresh.
	// Could be useful to reload profile or something related to user.
	// The callback will not be called for M2M.
	// If handler returned any error, user will be rejected with 403 code.
	PostRefresh func(writer http.ResponseWriter, req *http.Request, idToken *oidc.IDToken) error
	// (optional) tune allowed authorization types. Default - AllFlows
	Flows OAuthFlow
	// (optional) logger for messages, default is to std logger
	Logger Logger
}

type Level

type Level string
const (
	LogInfo  Level = "info"
	LogError Level = "error"
	LogWarn  Level = "warn"
)

type Logger

type Logger interface {
	Log(level Level, message string)
}

type LoggerFunc

type LoggerFunc func(level Level, message string)

func (LoggerFunc) Log

func (lf LoggerFunc) Log(level Level, message string)

type OAuthFlow

type OAuthFlow uint8

OAuthFlow represents set of allowed OAuth flows.

const (
	ClientCredentials OAuthFlow = 0b01 // client provides ID token in Authorization header (Bearer)
	AuthorizationCode OAuthFlow = 0b10 // UI flow with redirects. This flow will always be checked last since it can initiate redirects.
	AllFlows          OAuthFlow = AuthorizationCode | ClientCredentials
)

type OIDC

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

func New

func New(ctx context.Context, cfg Config) (*OIDC, error)

New creates new service which handles OIDC (OAuth 2) authorization. It will fetch and cache OIDC information on init. Keys rotation will be done automatically.

Service does provide automatic ID token refresh for UI flow.

func (*OIDC) Config

func (svc *OIDC) Config(req *http.Request) *oauth2.Config

Config for OAUTH. Request is required for detecting public server URL (unless it is defined explicitly).

func (*OIDC) Secure

func (svc *OIDC) Secure(next http.Handler) http.Handler

Secure handler by checking authorization state.

If ClientCredentials enabled in [Config.Flows], and Authorization header present, service assumes ClientCredentials flow. In this case, invalid request will cause 401 if token invalid, or 403 if post-auth callback returned an error.

If AuthorizationCode enabled in [Config.Flows], service will try AuthorizationCode flow. In this case, invalid request will cause login sequence and redirect to IDP.

Current ID token get be obtained by Token from request.

func (*OIDC) SecureFunc

func (svc *OIDC) SecureFunc(next http.HandlerFunc) http.Handler

SecureFunc is just an alias to OIDC.Secure for functional handlers.

func (*OIDC) ServeHTTP

func (svc *OIDC) ServeHTTP(writer http.ResponseWriter, request *http.Request)

ServeHTTP handling routes for authorization flows:

  • Callback: <prefix>/callback
  • Logout: <prefix>/logout

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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