Skip to content
fox-toolkitPublic

About

OpenID Connect Relying Party to build authentication gateways with Fox, such as Backend for Frontend (BFF) or Identity-Aware Proxy (IAP)

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

49 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

RP

Go Reference tests codecov GitHub release (latest SemVer) GitHub go.mod Go version

RP is an open-source OpenID Connect Relying Party for the Fox router. It provides the handlers and middleware to build authentication gateways such as Backend for Frontend (BFF), as described in OAuth 2.0 for Browser-Based Applications, or Identity-Aware Proxy (IAP).

RP supports any OpenID Provider through OpenID Connect Discovery, with sessions stored in encrypted cookies or in a server-side store, such as Redis or any implementation of the Store interface.

Disclaimer

The current API is not yet stabilized. Breaking changes may occur before v1.0.0 and will be noted on the release note.

Getting started

Installation

go get -u github.com/fox-toolkit/rp

Usage

Backend for Frontend

package main

import (
	"context"
	"log"
	"net/http"
	"net/http/httputil"
	"net/url"
	"os"

	"github.com/fox-toolkit/rp"
	"github.com/fox-toolkit/fox"
	"github.com/tink-crypto/tink-go/v2/aead/subtle"
)

func main() {
	provider, err := rp.NewProvider(context.Background(), "https://idp.example.com")
	if err != nil {
		log.Fatal(err)
	}
	
	aead, err := subtle.NewAESGCM([]byte(os.Getenv("BFF_KEY")))
	if err != nil {
		log.Fatal(err)
	}

	fe, err := rp.NewFrontend(rp.Client{
		Provider:              provider,
		ClientID:              "app",
		ClientSecret:          os.Getenv("CLIENT_SECRET"),
		RedirectURL:           "https://app.example.com/bff/callback",
		PostLogoutRedirectURL: "https://app.example.com/",
		Scopes:                []string{"openid", "profile"},
	}, aead)
	if err != nil {
		log.Fatal(err)
	}
	
	api, _ := url.Parse("https://api.example.com")
	proxy := &httputil.ReverseProxy{
		Rewrite: func(r *httputil.ProxyRequest) {
			r.SetURL(api)
		},
		Transport: rp.Transport(nil),
	}

	f := fox.MustRouter()
	f.MustAdd(fox.MethodGet, "/bff/login", fe.LoginHandler())
	f.MustAdd(fox.MethodGet, "/bff/callback", fe.CallbackHandler())
	f.MustAdd([]string{http.MethodGet, http.MethodPost}, "/bff/logout", fe.LogoutHandler())
	f.MustAdd(fox.MethodGet, "/bff/user", fe.UserHandler())
	
	// Proxy to the resource server, with the user's access token.
	f.MustAdd(fox.MethodAny, "/api/*{path}", fox.WrapH(proxy), fox.WithMiddleware(fe.Authenticate()))

	// The browser-based application is served here by the BFF. For other deployments, see
	// https://www.rfc-editor.org/rfc/rfc10017.html#section-6.1.2.5. A cross-origin
	// application requires rp.WithTrustedOrigins and a CORS middleware that allows its origin with credentials.
	f.MustAdd(fox.MethodGet, "/*{filepath}", fox.WrapH(http.FileServer(http.Dir("dist"))))
	
	log.Fatal(http.ListenAndServeTLS(":8443", "cert.pem", "key.pem", f))
}

Identity-Aware Proxy

p, err := rp.NewProxy(rp.Client{
	Provider:              provider,
	ClientID:              "dashboard",
	ClientSecret:          os.Getenv("CLIENT_SECRET"),
	RedirectURL:           "https://dashboard.example.com/oauth2/callback",
	PostLogoutRedirectURL: "https://dashboard.example.com/",
	Scopes:                []string{"openid", "email", "groups"},
}, aead,
	rp.WithAccessRule(rp.EmailDomain("example.com")),
	rp.WithIdentityHeader("X-Forwarded-Email", "email"),
	rp.WithIdentityHeader("X-Forwarded-Groups", "groups"),
)
if err != nil {
	log.Fatal(err)
}

upstream, _ := url.Parse("http://dashboard.internal:8080")
proxy := &httputil.ReverseProxy{
	Rewrite: func(r *httputil.ProxyRequest) {
		r.SetURL(upstream)
		r.SetXForwarded()
		r.Out.Host = r.In.Host
	},
}

f := fox.MustRouter()
f.MustAdd(fox.MethodGet, "/oauth2/callback", p.CallbackHandler())
f.MustAdd(fox.MethodAny, "/oauth2/logout", p.LogoutHandler())

// A navigation without a session starts the OpenID Connect Authorization Code Flow, see
// https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth.
f.MustAdd(fox.MethodAny, "/*{path}", fox.WrapH(proxy), fox.WithMiddleware(p.Authenticate()))

Open Standards

RP implements Open Standards set by the IETF:

and the OpenID Foundation:

Conformance

RP passes the client test plans of the OpenID Foundation conformance suite v5.3.4 (see the conformance module). The FAPI 2.0 Security Profile and FAPI 2.0 Message Signing plans use the configuration below, and the OpenID Connect Basic, Config, refresh token, Back-Channel Logout and RP-Initiated Logout plans use a client secret.

fe, err := rp.NewFrontend(rp.Client{
	Provider:    provider,
	ClientID:    "app",
	ClientKey:   key, // EC P-256, RSA or Ed25519 private key
	RedirectURL: "https://app.example.com/bff/callback",
	Scopes:      []string{"openid"},
}, aead,
	rp.WithPushedAuthorization(rp.PushedAuthorizationRequired),
	rp.WithDPoP(true),
	rp.WithRequestObject(true), // FAPI 2.0 Message Signing only
	rp.WithSigningAlgorithms("PS256", "ES256", "EdDSA"),
)

License

RP is licensed under the MIT License. See LICENSE for details.

The RP logo is licensed separately under CC BY-NC-ND 4.0. See LICENSE-rp-logo.txt for details.

About

OpenID Connect Relying Party to build authentication gateways with Fox, such as Backend for Frontend (BFF) or Identity-Aware Proxy (IAP)

Topics

Resources

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages