jambo

package module
v0.0.8 Latest Latest
Warning

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

Go to latest
Published: Sep 29, 2025 License: MIT Imports: 24 Imported by: 0

README

jambo

jambo is an Go package to build an OpenID Connect provider (OIDC server).

"Jambo" is also a Swahili word. It translates to "hello" or "hi". It's a common greeting used in East Africa, particularly in Tanzania and Kenya.

In order to create a OpenID Connect provider, you will have to create a new server, which is in turn a HTTP handler.

This is a complete example:

package main

import (
	"github.com/cespedes/jambo"
)

func main() {
	issuer := "https://example.com/oidc"
	root := "/oidc"
	s := jambo.NewServer(issuer, root)

	clientID := "test-client"
	clientSecret := "client-secret"
	s.AddClient(clientID, clientSecret)

	s.SetAuthenticator(func (req *jambo.Request) jambo.Response {
		if req.Params["login"] == "admin" && req.Params["password"] == "secret" {
			return jambo.Response{
				Type: jambo.ResponseTypeLoginOK,
				Login:  "admin",
				Name:   "Charlie Root",
				Claims: map[string]any{},
			}
		}
		return jambo.Response{
			Type: jambo.ResponseTypeLoginFailed,
			Login: req.Params["login"],
		}
	})

	http.ListenAndServe(":8080", s)
}

It will create a HTTP server, listening to requests under root and providing all the necessary handlers for the OIDC provider.


The OpenID Connect specification is here:

  • https://openid.net/specs/openid-connect-discovery-1_0.html
  • https://openid.net/specs/openid-connect-core-1_0.html

HTTP server endpoints

endpoint description
/.well-known/openid-configuration OpenID Connect configuration
/auth HTML page to ask for credentials
POST /auth/login to send login information (password, OTP...) to the server
POST /token used by clients to send the code and get access token
/keys get the list of keys used to sign the tokens

Workflow

We will assume Alice (client) wants to connect to a GitLab instance (client), which is configured to authenticate using Jambo, our OpenID Connect provider.

  • Alice opens a web browser and goes to GitLab page (https://gitlab.example.com).
  • GitLab redirects to Jambo's authentication page (https://jambo.example.com/auth), with query parameters specifying the client (GitLab) and the list of the required scopes.
  • Jambo reads the query parameters and checks if the client exists and the URL is well-formed.
  • Jambo creates a session for this connection and stores its state.
  • Jambo parses a HTML template and offers it to Alice a login page (typically with a HTML form).
  • Alice fills the user and password and presses "submit".
  • The form is posted to the authentication page (https://jambo.example.com/auth/request).
  • Jambo receives the request, checks if it somes from an active session, and calls the Authenticator function with all the parameters received from the form.
  • The Authentication function checks the parameters and returns a "Login OK".
  • Jambo optionally redirects to an approval HTML template, with a summary and a way to continue to the client (GitLab).
  • When Alice clicks "OK", Jambo redirects to the GitLab's "callback address"
  • GitLab receives the request with a "code"
  • GitLab connects to Jambo in background, sending the "code" and the "client secret".
  • Jambo replies with an access token which contains a BASE64 signed JSON object with the claims (login, name, e-mail...) depending on the requested scopes.
  • GitLab receives the response and sends Alice the GitLab page, already authenticated.

Other OpenID Connect providers

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Client

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

func (*Client) AddAllowedRedirectURIs added in v0.0.2

func (c *Client) AddAllowedRedirectURIs(names ...string)

func (*Client) AddAllowedRoles added in v0.0.2

func (c *Client) AddAllowedRoles(names ...string)

AddAllowedRoles adds one or more roles to the list of the allowed roles for users. If there are no allowed roles, any user can log in. If there is at least one, the users must belong to one of them.

func (*Client) AddAllowedScopes added in v0.0.2

func (c *Client) AddAllowedScopes(names ...string)

type Connection

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

type IDToken

type IDToken struct {
	// Standard claims:
	Issuer            string `json:"iss"`
	SubjectIdentifier string `json:"sub"`
	Audience          string `json:"aud"`
	Expiration        int64  `json:"exp"`
	IssuedAt          int64  `json:"iat"`
	Nonce             string `json:"nonce,omitempty"`
	PreferredUsername string `json:"preferred_username,omitempty"`
	Name              string `json:"name,omitempty"`
	Email             string `json:"email,omitempty"`
	EmailVerified     bool   `json:"email_verified,omitempty"`

	// Other claims:
	Claims map[string]any
}

https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.2

func (IDToken) MarshalJSON added in v0.0.2

func (idt IDToken) MarshalJSON() ([]byte, error)

type Request

type Request struct {
	Session string // unique ID for this user.
	Client  string
	Scopes  []string // scopes the user has requested
	Roles   []string // list of allowed roles
	Params  map[string]string
}

A Request is a message sent from the OIDC server to the authenticator, asking if a given credentials are valid

type Response

type Response struct {
	Type ResponseType

	// If Type == ResponseTypeLoginFailed we can send an error to the user:
	Error string

	// If Type == ResponseTypeRedirect we need the name of the next template
	// and the list of params that will be used to call it:
	Redirect string
	Params   map[string]string

	// login for the user. It is usually the same sent in the request.
	// Used in claims "sub" and "preferred_username".
	Login string

	// User name and surname.  Used in claim "name".
	Name string

	// e-mail address.  Used in claim "email".
	Mail string

	// Other claims:
	Claims map[string]any
}

A Response is sent from the authenticator to the OIDC server, answering a Request.

type ResponseType

type ResponseType int
const (
	ResponseTypeInvalid     ResponseType = iota
	ResponseTypeLoginOK                  // login is successful
	ResponseTypeLoginFailed              // login failed
	ResponseTypeRedirect                 // login is OK so far, but we are not finished yet
)

type Server

type Server struct {
	sync.Mutex // to access clients and connections
	// contains filtered or unexported fields
}

func NewServer

func NewServer(issuer, root string) *Server

func (*Server) AddStaticFS

func (s *Server) AddStaticFS(filesystem fs.FS)

AddStaticFS adds the content of a filesystem (a fs.FS) to the list of static files served by a Server.

func (*Server) AddTemplateArg added in v0.0.5

func (s *Server) AddTemplateArg(key, value string)

func (*Server) AddTemplatesFS added in v0.0.2

func (s *Server) AddTemplatesFS(filesystem fs.FS) error

AddTemplatesFS adds the files inside a fs.FS to the list of templates processed by a Server.

func (*Server) GetConnection added in v0.0.2

func (s *Server) GetConnection(r *http.Request) *Connection

SetConnection gets a Connection previously stored in a http.Request

func (*Server) NewClient added in v0.0.2

func (s *Server) NewClient(name, secret string) *Client

func (*Server) ServeHTTP

func (s *Server) ServeHTTP(w http.ResponseWriter, r *http.Request)

func (*Server) SetAuthenticator added in v0.0.3

func (s *Server) SetAuthenticator(f func(req *Request) Response)

func (*Server) SetConnection added in v0.0.2

func (s *Server) SetConnection(r *http.Request, conn *Connection) *http.Request

SetConnection stores a Connection in a http.Request

Directories

Path Synopsis
cmd
oidc-server command
Package mergefs provides support for merging multiple filesystems together.
Package mergefs provides support for merging multiple filesystems together.

Jump to

Keyboard shortcuts

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