idp

package module
v0.0.7 Latest Latest
Warning

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

Go to latest
Published: Oct 20, 2024 License: GPL-3.0 Imports: 0 Imported by: 0

README

idp

smol/idp is a full-blown authentication stack that offers an OpenID Connect API. It includes all the functionality required to operate a (relatively basic) identity provider.

Feature highlights:

  • Extensive OpenID Connect support (by way of zitadel/oidc)
  • Modern login UI for web protocols:
    • 2FA support
      • TOTP
      • WebAuthN
    • Account recovery (one-time tokens, email validation)
    • Ability to enforce post-login workflows (password resets, notifications, etc)
    • Audit logs and new device detection
  • User management UI for self-service credentials management
  • Application-specific passwords for non-interactive protocols
  • Minimal storage requirements
  • Customizable SQL user backend

Why?

The authentication space already provides a number of good, featureful solutions. So if you're looking for something of a professional quality you should definitely look into one of:

This project targets a minimalistic resource (and operational) environment, so it makes different compromises than most other solutions out there:

  • Minimal storage requirements: instead of requiring a separate SQL server for operation, we wanted something capable of working with an embedded SQLite backend (where fault tolerance can be provided by something like Litestream).

  • Simpler data dependencies: authentication systems ultimately require integration with larger-scoped user management systems and existing user databases. As a scalable solution, we provide a fully customizable SQL user backend, along with a default, ready-to-use SQLite embedded implementation. The user database can even be scaled down to "static JSON file" for even simpler deployments.

  • Modularity: to simplify deployments, while allowing the design of fault tolerant architectures, we've split the design into components that can be run separately. This allows a separation of stateful vs. stateless components, as well as short-term and long-term data storages, all of which imply potentially different fault tolerance strategies.

Architecture

The stack consists of a few independent layers that talk to each other via GRPC. Some components require their own, independently managed storage. You should run a single instance of these, if you're using the default SQLite database backend!

Architecture diagram

User database

Most components need access to the user database. There are two possibilities to consider:

  • There is no extant, external user database, your service doesn't require one, perhaps you are only setting up authentication for a small group of admins and don't require any "business logic" on top of that;

  • your organization already has a database of user accounts, possibly with additional information related to other services or business requirements.

Stand-alone (static)

The user database can be just a static JSON file, with a list of users and their associated credentials (including two-factor authentication).

This is a good fit for a small group of operators, where authentication controls access to administrative resources, and you are running a service with no "users", or where SSO integration is not necessary. Then, all changes to authentication credentials have to go through source control, which comes with its own additional audit mechanisms.

Stand-alone (dynamic)

The account server can manage its own database, usually running it as an embedded SQLite database.

This option currently requires you to use the account server component (see below).

Integration with an existing database

The user database layer can be configured to talk to an external Postgres SQL server. Individual queries can be configured to adapt to whatever schema the existing database might have.

Integration via RPC

For even more customization, it is possible to have idp talk to your user database over GRPC. You can then implement the Account service (cf. account/proto/account.proto) yourself.

An example of such an implementation is the Account server (see below), a built-in idp component that exposes a stand-alone user database over GRPC.

Mapping the user database model to Oauth2

The smol/idp user database model includes three main identifiers for users: an opaque, unique ID, a username, and an email address. The software however makes no assumptions on their semantics, allowing support for a wide range of use cases.

The only requirement is that the primary unique user identifier does not change. The software uses the user ID as the identity principal in the generated OAuth tokens, while the other identity attributes can be retrieved via profile or openid claims.

The simplest user databases might have all three identifiers set to the same value (for the standard scenario of "email as username"), or perhaps just the first two (when no email integration is required), while more sophisticated solutions will use a database-level unique identifier for the ID, allowing username / email changes while maintaining the same identity.

Authentication server

The authentication server API is documented in authn/proto/authn.proto and offers an interface for low-level user authentication, supporting multiple services (in the style of PAM) with potentially different backends and parameters.

It requires access to a short-term storage for ratelimits, anti-replay protection and WebAuthN sessions. This can either be Memcache (multiple servers supported), or just an in-memory cache.

Account server

While normally every service manages its own connection to the user database, this is not possible if you want to use a SQLite database and want to run components as separate processes. In this scenario, it is possible to run the account server, which owns the SQLite database and provides a GRPC API that other services can connect to.

Audit server

The audit server stores login and account change information long-term, in order to provide an audit trail for logins, and to provide analytical insights on the suspiciousness of user logins (for instance, to notify users of logins from new / unknown devices).

The server uses its own SQLite database, which is periodically compacted (removing old / unnecessary entries) to avoid unrestricted growth over time.

Login UI

The login application lets users perform the authentication workflow. The actual authentication is delegated to the authentication server.

The OIDC machinery requires its own storage for registered tokens and other data (currently not split between short-term-only and long-term, though this is planned).

Management UI

This component can be thought of as a standalone application, tightly integrated with the login workflow: it lets users manage their authentication credentials.

Note that it is not a full user management solution, it is focused exclusively on self-service credentials management. It offers no way of, for instance, creating new users or managing users in bulk, as normally these operations require additional business logic.

Running

The idp service is shipped as a single binary (smol-idp), which supports a number of commands to select the service component to run:

  • auth - run the Authentication server
  • audit - run the Audit server
  • account - run the Account server
  • idp - run the HTTP service (OIDC and management UI)
  • bundle - run the auth, audit and idp services together in a single stand-alone process

All commands support an --addr command-line option to specify the listening address, and a --metrics-addr option for a separate HTTP listener to export Prometheus metrics.

Server commands take a configuration file, in YAML format. Configuration can be fairly complex, so each command provides a --help-config option that will provide further, detailed documentation on the configuration structure and semantics. Furthermore, most configuration variables (with the exception of list-like containers) can be set via environment variables, also documented in the --help-config output.

Example bundle configuration

A simple example of a bundle.yml configuration file for the all-in-one service setup:

userdb:
  type: static
  params:
    users:
      - name: admin
        password: "$argon2id$..."
        email: admin@example.com
        webauthn_registrations:
          - public_key: ...
            key_handle: ...
            comment: "my little hardware token"
authn:
  services:
    login:
      interactive: true
      webauthn:
        display_name: Example
        rpid: https://auth.example.com
        origin: auth.example.com
    confirm:
      disable_audit: true
oidc:
  db_path: "/data/oidc.db"
  issuer: "https://auth.example.com"
  encryption_key: "<random 32-byte string>"
  clients:
    myapp:
      name: "My App"
      type: web
      secret: "..."
      redirect_uris:
        - "https://my-app.example.com/oauth2/callback"
login:
  auth_service: login
mgmt:
  confirm_auth_service: confirm
  webauthn:
    display_name: Example
    rpid: https://auth.example.com
    origin: auth.example.com

This defines a static user database with a single "admin" user, and sets up an OpenID Connect issuer at https://auth.example.com, with a "myapp" static client. The login service is configured for WebAuthN support.

mTLS

The services support mutual TLS authentication and authorization, see MTLS.md for further details.

Tools for managing static user databases

Hashed passwords should be in a format that the underlying password library can understand. Argon2 algorithms are currently supported. It is possible to generate such a hash with the pwhash command:

smol-idp pwhash $PASSWORD

Generation of static WebAuthN registrations isn't currently supported by this tool, but you can use webauthn-cred for that:

go install git.autistici.org/ai3/tools/webauthn-cred@latest
webauthn-cred --rpid auth.example.com

Documentation

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

This section is empty.

Types

type Features

type Features struct {
	EnableOTP             bool `yaml:"enable_otp" doc:"enable TOTP 2FA"`
	EnableWebAuthn        bool `yaml:"enable_webauthn" doc:"enable WebAuthN 2FA"`
	EnableASPs            bool `yaml:"enable_app_specific_passwords" doc:"enable app-specific passwords for non-interactive services"`
	EnableMessages        bool `yaml:"enable_messages" doc:"enable broadcast messages"`
	Enforce2FA            bool `yaml:"enforce_2fa" doc:"enforce setup of 2FA for all users"`
	EnableAccountRecovery bool `yaml:"enable_account_recovery" doc:"enable account recovery"`

	Insecure bool `yaml:"insecure" doc:"disable various security measures (cookies, OIDC, ...)"`
}

Directories

Path Synopsis
cmd
internal
web
Package login implements the authentication workflow, protecting another application by means of its Wrap() middleware.
Package login implements the authentication workflow, protecting another application by means of its Wrap() middleware.

Jump to

Keyboard shortcuts

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