All Projects → XenitAB → go-oidc-middleware

XenitAB / go-oidc-middleware

Licence: MIT license
OpenID Connect (OIDC) http middleware for Go

Programming Languages

go
31211 projects - #10 most used programming language
Makefile
30231 projects

Projects that are alternatives of or similar to go-oidc-middleware

sotsera.blazor.oidc
OpenID Connect client for Blazor client-side projects
Stars: ✭ 21 (-67.69%)
Mutual labels:  openid-connect, oidc
oidc-agent
oidc-agent for managing OpenID Connect tokens on the command line
Stars: ✭ 47 (-27.69%)
Mutual labels:  openid-connect, oidc
aws-cdk-github-oidc
CDK constructs to use OpenID Connect for authenticating your Github Action workflow with AWS IAM
Stars: ✭ 59 (-9.23%)
Mutual labels:  openid-connect, oidc
mock-oauth2-server
A scriptable/customizable web server for testing HTTP clients using OAuth2/OpenID Connect or applications with a dependency to a running OAuth2 server (i.e. APIs requiring signed JWTs from a known issuer)
Stars: ✭ 83 (+27.69%)
Mutual labels:  openid-connect, oidc
Authlib
The ultimate Python library in building OAuth, OpenID Connect clients and servers. JWS,JWE,JWK,JWA,JWT included.
Stars: ✭ 2,854 (+4290.77%)
Mutual labels:  openid-connect, oidc
Node Oidc Provider
OpenID Certified™ OAuth 2.0 Authorization Server implementation for Node.js
Stars: ✭ 2,018 (+3004.62%)
Mutual labels:  openid-connect, oidc
auth-backends
Custom authentication backends and views for edX services
Stars: ✭ 20 (-69.23%)
Mutual labels:  openid-connect, oidc
example-oidc
OIDC (OpenID Connect) Example for http://openid.net/connect/
Stars: ✭ 221 (+240%)
Mutual labels:  openid-connect, oidc
Oauthlib
A generic, spec-compliant, thorough implementation of the OAuth request-signing logic
Stars: ✭ 2,323 (+3473.85%)
Mutual labels:  openid-connect, oidc
Hydra
OpenID Certified™ OpenID Connect and OAuth Provider written in Go - cloud native, security-first, open source API security for your infrastructure. SDKs for any language. Compatible with MITREid.
Stars: ✭ 11,884 (+18183.08%)
Mutual labels:  openid-connect, oidc
AspNetCore6Experiments
ASP.NET Core Blazor BFF with Azure AD and Razor page
Stars: ✭ 43 (-33.85%)
Mutual labels:  openid-connect, oidc
oidc
Easy to use OpenID Connect client and server library written for Go and certified by the OpenID Foundation
Stars: ✭ 475 (+630.77%)
Mutual labels:  openid-connect, oidc
AspNetCoreBackChannelLogout
ASP.NET Core Back-Channel Logout for Hybrid Clients, Redis, Key Vault, Azure
Stars: ✭ 17 (-73.85%)
Mutual labels:  oidc
okta-react-native-spring-boot-example
React Native + Spring Boot + OIDC
Stars: ✭ 24 (-63.08%)
Mutual labels:  oidc
angular-openid-connect-php
Angular & PHP CodeIgniter server through OAuth 2.0 OpenID Connect
Stars: ✭ 14 (-78.46%)
Mutual labels:  openid-connect
angular-auth-oidc-sample-google-openid
Angular oidc client with google Identity OpenID
Stars: ✭ 23 (-64.62%)
Mutual labels:  oidc
keycloak-springsecurity5-sample
Spring Security 5 OAuth2 Client/OIDC integration with Keycloak sample
Stars: ✭ 55 (-15.38%)
Mutual labels:  oidc
wired-vpn
WireGuard behind OIDC
Stars: ✭ 21 (-67.69%)
Mutual labels:  oidc
SATOSA
Proxy translating between different authentication protocols (SAML2, OpenID Connect and OAuth2)
Stars: ✭ 139 (+113.85%)
Mutual labels:  oidc
beyondauth
a traefik / nginx companion to create an identity aware proxy like beyondcorp
Stars: ✭ 26 (-60%)
Mutual labels:  openid-connect

Go OpenID Connect (OIDC) HTTP Middleware

Coverage Status

Introduction

This is a middleware for http to make it easy to use OpenID Connect.

Stability notice

This library is under active development and the api will have breaking changes until v0.1.0 - after that only breaking changes will be introduced between minor versions (v0.1.0 -> v0.2.0).

Currently tested providers

  • Azure AD
  • Auth0
  • Okta
  • Cognito

Currently Supported frameworks

Using options

Import: "github.com/xenitab/go-oidc-middleware/options"

net/http, mux & chi

Import

"github.com/xenitab/go-oidc-middleware/oidchttp"

Middleware

oidcHandler := oidchttp.New(h,
	options.WithIssuer(cfg.Issuer),
	options.WithRequiredTokenType("JWT"),
	options.WithRequiredAudience(cfg.Audience),
	options.WithFallbackSignatureAlgorithm(cfg.FallbackSignatureAlgorithm),
	options.WithRequiredClaims(map[string]interface{}{
		"tid": cfg.TenantID,
	}),
)

Handler

func newClaimsHandler() http.HandlerFunc {
	fn := func(w http.ResponseWriter, r *http.Request) {
		claims, ok := r.Context().Value(options.DefaultClaimsContextKeyName).(map[string]interface{})
		if !ok {
			w.WriteHeader(http.StatusUnauthorized)
			return
		}

		w.Header().Set("Content-Type", "application/json")
		err := json.NewEncoder(w).Encode(claims)
		if err != nil {
			w.WriteHeader(http.StatusInternalServerError)
			return
		}
	}

	return http.HandlerFunc(fn)
}

gin

Import

"github.com/xenitab/go-oidc-middleware/oidcgin"

Middleware

oidcHandler := oidcgin.New(
	options.WithIssuer(cfg.Issuer),
	options.WithRequiredTokenType("JWT"),
	options.WithRequiredAudience(cfg.Audience),
	options.WithFallbackSignatureAlgorithm(cfg.FallbackSignatureAlgorithm),
	options.WithRequiredClaims(map[string]interface{}{
		"tid": cfg.TenantID,
	}),
)

Handler

func newClaimsHandler() gin.HandlerFunc {
	return func(c *gin.Context) {
		claimsValue, found := c.Get("claims")
		if !found {
			c.AbortWithStatus(http.StatusUnauthorized)
			return
		}

		claims, ok := claimsValue.(map[string]interface{})
		if !ok {
			c.AbortWithStatus(http.StatusUnauthorized)
			return
		}

		c.JSON(http.StatusOK, claims)
	}
}

fiber

Import

"github.com/xenitab/go-oidc-middleware/oidcfiber"

Middleware

oidcHandler := oidcfiber.New(
	options.WithIssuer(cfg.Issuer),
	options.WithRequiredTokenType("JWT"),
	options.WithRequiredAudience(cfg.Audience),
	options.WithFallbackSignatureAlgorithm(cfg.FallbackSignatureAlgorithm),
	options.WithRequiredClaims(map[string]interface{}{
		"tid": cfg.TenantID,
	}),
)

Handler

func newClaimsHandler() fiber.Handler {
	return func(c *fiber.Ctx) error {
		claims, ok := c.Locals("claims").(map[string]interface{})
		if !ok {
			return c.SendStatus(fiber.StatusUnauthorized)
		}

		return c.JSON(claims)
	}
}

Echo (JWT ParseTokenFunc)

Import

"github.com/xenitab/go-oidc-middleware/oidcechojwt"

Middleware

e.Use(middleware.JWTWithConfig(middleware.JWTConfig{
    ParseTokenFunc: oidcechojwt.New(
		options.WithIssuer(cfg.Issuer),
		options.WithRequiredTokenType("JWT"),
		options.WithRequiredAudience(cfg.Audience),
		options.WithFallbackSignatureAlgorithm(cfg.FallbackSignatureAlgorithm),
		options.WithRequiredClaims(map[string]interface{}{
			"tid": cfg.TenantID,
		}),
	),
}))

Handler

func newClaimsHandler(c echo.Context) error {
	claims, ok := c.Get("user").(map[string]interface{})
	if !ok {
		return echo.NewHTTPError(http.StatusUnauthorized, "invalid token")
	}

	return c.JSON(http.StatusOK, claims)
}

Build your own middleware

Import

"github.com/xenitab/go-oidc-middleware/oidctoken"

Example

oidcTokenHandler := oidctoken.New(h,
	options.WithIssuer(cfg.Issuer),
	options.WithRequiredTokenType("JWT"),
	options.WithRequiredAudience(cfg.Audience),
	options.WithFallbackSignatureAlgorithm(cfg.FallbackSignatureAlgorithm),
	options.WithRequiredClaims(map[string]interface{}{
		"tid": cfg.TenantID,
	}),
)

// oidctoken.GetTokenString is optional, but you will need the JWT token as a string
tokenString, err := oidctoken.GetTokenString(...)
if err != nil {
	panic(err)
}

token, err := oidcTokenHandler.ParseToken(ctx, tokenString)
if err != nil {
	panic(err)
}

Other options

Deeply nested required claims

If you want to use options.WithRequiredClaims() with nested values, you need to specify the actual type when configuring it and not an interface and the middleware will use this to infer what types the token claims are.

Example claims could look like this:

{
  "foo": {
    "bar": ["uno", "dos", "baz", "tres"]
  }
}

This would then be interpreted as the following inside the code:

"foo": map[string]interface {}{
	"bar":[]interface {}{
		"uno",
		"dos",
		"baz",
		"tres"
	},
}

If you want to require the claim foo.bar to contain the value baz, it would look like this:

options.WithRequiredClaims(map[string]interface{}{
	"foo": map[string][]string{
		"bar": {"baz"},
	}
})

Extract token from multiple headers

Example for Authorization and Foo headers. If token is found in Authorization, Foo will not be tried. If Authorization extraction fails but there's a header Foo = Bar_baz then baz would be extracted as the token.

oidcHandler := oidcgin.New(
	options.WithIssuer(cfg.Issuer),
	options.WithFallbackSignatureAlgorithm(cfg.FallbackSignatureAlgorithm),
	options.WithRequiredClaims(map[string]interface{}{
		"cid": cfg.ClientID,
	}),
	options.WithTokenString(
		options.WithTokenStringHeaderName("Authorization"),
		options.WithTokenStringTokenPrefix("Bearer "),
	),
	options.WithTokenString(
		options.WithTokenStringHeaderName("Foo"),
		options.WithTokenStringTokenPrefix("Bar_"),
	),
)

Manipulate the token string after extraction

If you want to do any kind of manipulation of the token string after extraction, the option WithTokenStringPostExtractionFn is available.

The following would be used by a the Kubernetes api server, where the kubernetes client can use both Authorization and Sec-WebSocket-Protocol.

oidcHandler := oidcgin.New(
	options.WithIssuer(cfg.Issuer),
	options.WithFallbackSignatureAlgorithm(cfg.FallbackSignatureAlgorithm),
	options.WithRequiredClaims(map[string]interface{}{
		"cid": cfg.ClientID,
	}),
	options.WithTokenString(
		options.WithTokenStringHeaderName("Authorization"),
		options.WithTokenStringTokenPrefix("Bearer "),
	),
	options.WithTokenString(
		options.WithTokenStringHeaderName("Sec-WebSocket-Protocol"),
		options.WithTokenStringTokenPrefix("base64url.bearer.authorization.k8s.io."),
		options.WithTokenStringListSeparator(","),
		options.WithTokenStringPostExtractionFn(func(s string) (string, error) {
			bytes, err := base64.RawStdEncoding.DecodeString(s)
			if err != nil {
				return "", err
			}

			return string(bytes), nil
		}),
	),
)

Custom error handler

It is possible to add a custom function to handle errors. It will not be possible to change anything using it, but you will be able to add logic for logging as an example.

errorHandler := func(description options.ErrorDescription, err error) {
	fmt.Printf("Description: %s\tError: %v\n", description, err)
}

oidcHandler := oidcgin.New(
	options.WithIssuer(cfg.Issuer),
	options.WithFallbackSignatureAlgorithm(cfg.FallbackSignatureAlgorithm),
	options.WithRequiredClaims(map[string]interface{}{
		"cid": cfg.ClientID,
	}),
	options.WithErrorHandler(errorHandler),
)

Testing with the middleware enabled

There's a small package that simulates an OpenID Provider that can be used with tests.

package main

import (
	"testing"

	"github.com/xenitab/go-oidc-middleware/optest"
)

func TestFoobar(t *testing.T) {
	op := optest.NewTesting(t)
	defer op.Close(t)

	[...]

	oidcHandler := oidchttp.New(h,
		options.WithIssuer(op.GetURL(t)),
		options.WithRequiredTokenType("JWT+AT"),
		options.WithRequiredAudience("test-client"),
	)

	token := op.GetToken(t)

	[...]

	token.SetAuthHeader(req)

	[...]
}

You can also configure multiple users by setting the following:

func TestFoobar(t *testing.T) {
	testUsers := map[string]TestUser{
		"test": {
			Audience:           "test-client",
			Subject:            "test",
			Name:               "Test Testersson",
			GivenName:          "Test",
			FamilyName:         "Testersson",
			Locale:             "en-US",
			Email:              "[email protected]",
			AccessTokenKeyType: "JWT+AT",
			IdTokenKeyType:     "JWT",
		},
		"foo": {
			Audience:           "foo-client",
			Subject:            "foo",
			Name:               "Foo Bar",
			GivenName:          "Foo",
			FamilyName:         "Bar",
			Locale:             "en-US",
			Email:              "[email protected]",
			AccessTokenKeyType: "JWT+AT",
			IdTokenKeyType:     "JWT",
		},
	}

	op := optest.NewTesting(t, optest.WithTestUsers(testUsers), optest.WithDefaultTestUser("test"))
	defer op.Close(t)

	[...]

	token1 := op.GetToken(t) // for user `test`
	token2 := op.GetTokenByUser(t, "test") // for user `test`
	token3 := op.GetTokenByUser(t, "foo") // for user `foo`
}

It is also possible to enable opaque access tokens with the option optest.WithOpaqueAccessTokens().

Examples

See examples readme for more information.

Roadmap

GitHub Project

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].