Giter Club home page Giter Club logo

obligator's Introduction

Introduction

obligator is a relatively simple and opinionated OpenID Connect (OIDC) Provider (OP) server designed for self-hosters.

Hacker News discussion here.

Motivation

There are lots of great open source OIDC servers out there (see comparison). I made obligator because I needed a specific combination of features I didn't find in any of the others. Here's a brief list. See the feature explanation section for more detailed information.

  • Simple to deploy and manage. Static executable and either flat-file or sqlite storage
  • Support for anonymous OAuth2 client auth
  • Authenticate to multiple domains at once
  • Passwordless email login
  • Configurable at runtime with an API
  • Support for forward auth
  • Support for trusted headers
  • Support for upstream social login providers (GitLab, GitHub, Google, etc)

Design

The overarching philosophy of obligator is that identities are built on email. Email isn't perfect, but it's the globally unique federated identity we have that works today.

Thus the purpose of obligator is to validate that a user controls an email address as simply as possible, and communicate that to the application the user is attempting to log in to. Validation can either be done directly through SMTP, or delegated to upstream OIDC (and some plain OAuth2) providers.

Running it

Here's a fairly complete JSON storage file (obligator_storage.json). Note that I call it "storage" and not "config" because it's not static, and more like a simple database. obligator will update it at runtime if new values are provided through the API.

{
  "root_uri": "https://example.com",
  "login_key_name": "obligator_login_key",
  "oauth2_providers": [
    {
      "id": "google",
      "name": "Google",
      "uri": "https://accounts.google.com",
      "client_id": "<google oauth2 client_id>",
      "client_secret": "<google oauth2 client_secret>",
      "openid_connect": true
    },
    {
      "id": "lastlogin",
      "name": "LastLogin.io",
      "uri": "https://lastlogin.io",
      "client_id": "https://example.com",
      "client_secret": "",
      "openid_connect": true
    }
  ],
  "smtp": {
    "server": "smtp.fastmail.com",
    "username": "<smtp-username>",
    "password": "<smtp-password>",
    "port": 587,
    "sender": "[email protected]",
    "sender_name": "Example"
  },
  "jwks": "<generated at first startup if empty>",
  "users": [
    {
      "email": "[email protected]"
    },
    {
      "email": "[email protected]"
    }
  ],
  "public": false
}

If you're already using docker, it's the easiest way to get started with obligator:

mkdir obligator_docker/
cp obligator_storage.json obligator_docker/

docker run --user $(id -u):$(id -g) --rm -it -v $PWD/obligator_docker:/data -v $PWD/obligator_docker:/api -p 1616:1616 anderspitman/obligator:latest -storage-dir /data -api-socket-dir /api -root-uri example.com -port 1616

You can also download static executables for various platforms from the releases page.

Using the API

Currently the API is only offered through unix sockets. This reduces the chance that it accidentally gets exposed, which is important because it's not authenticated in any way.

There's not any documentation, and the API is in flux, so refer to the source code for usage.

Here's an example assuming you ran the docker command above:

curl --unix obligator_docker/obligator_api.sock dummy-domain/oauth2-providers

See here for more info on using curl over unix sockets.

Support

Community support is provided on the IndieBits forums.

Feature explanation

Anonymous OAuth2 auth

Normally in OAuth2 (and therefore OIDC), an app (client) is required to pre-register with the provider. This can create a lot of friction, especially if you're self-hosting an open source application. App developers are forced to either share a single client ID for all their users (and share their client secret, which essentially makes it pointless), or each user must separately register their instance.

Instead, obligator takes essentially the approach described here. Any OAuth2 client can anonymously authenticate with an obligator instance, with the client_id equal to the domain of the client, and client_secret left blank. Security is maintained through the following means:

  • Only approved email addresses are permitted unless public: true is set in the config.
  • The client_id URI must be a prefix of the redirect_uri, and the client_id is displayed to the user when consenting to the login. This guarantees that the user approves the ID token to be sent to the domain shown. Note that this can actually be more secure than pre-registration. There have been attacks in the past where users were tricked into authorizing apps because the pre-registered information looked convincing. By forcing the user to decide whether they trust the actual domain where the ID token will be sent, and not displaying any sort of logo which can be faked, security is improved.

Note that some servers implement OIDC Dynamic Client Registration, which is an official specification to accomplish some of the same goals as anonymous auth. When an initial access token is not required (notably the case with Ory), this can result in a very similar experience from the user perspective. The main problem is that client apps must support dynamic client registration, and many don't. Anonymous auth does not require any special features on the client side.

Multi-domain authentication

Have you ever noticed when you login to Gmail on a new computer that you're also automatically logged in to YouTube? How does this work when Gmail is on google.com and youtube.com doesn't have any access to the cookies or localstorage of google.com?

The answer is that when you log in on accounts.google.com, it makes a quick redirect to youtube.com with a URL parameter to also set up the cookies there. I also want this functionality for all the domains protected by my OIDC server so I'm building it into obligator.

Passwordless email login

In line with the philosophy above, email reigns supreme in obligator. Since passwords are relatively difficult to use securely, the way to add an email identity is to send a confirmation code to the email address.

Demo

There's a public instance of obligator running at https://lastlogin.io (discovery doc at https://lastlogin.io/.well-known/openid-configuration). You can use it with any OIDC client. Just set the client_id to a prefix of the redirect_uri the client application uses when making the authorization request. I like to use https://openidconnect.net/ for ad-hoc testing, like so:

  1. Click on "Configuration" on the right side
  2. Enter the discovery document URL, ie https://lastlogin.io/.well-known/openid-configuration for LastLogin
  3. Click "Use Discovery Document". It should populate most of the fields
  4. Set the client_id to https://openidconnect.net/. This is a prefix of the redirect_uri that openidconnect.net uses, which is https://openidconnect.net/callback
  5. You can leave the client_secret as it is or remove it.
  6. Click "Save", then "Start" to begin the flow.

The official OpenID conformance suite is also excellent for testing OIDC servers.

Comparison is the thief of joy

Software is rarely about right vs wrong, but rather tradeoffs. This table is intended to help compare tradeoffs of different servers. It's also very incomplete and probably incorrect in many cases. If you have a correction, please submit an issue or leave a comment on the Google sheet here which is where it's generated from.

It's generated using the excellent https://tabletomarkdown.com

obligator Portier Rauthy Authelia Authentik KeyCloak Vouch oauth2-proxy Dex Ory Stack Zitadel Casdoor Kanidm
Simple
Anonymous auth
Multi-domain auth ✅ (planned)
Passwordless email login
HTTP API
Forward auth
Trusted header auth ✅ (planned)
Upstream OIDC/OAuth2 ❌ (partial)
SAML Needs coding
LDAP Needs coding
MFA
Standalone reverse proxy
Admin GUI
Dyanmic client registration
Passkey support
Vanity stars stars stars stars stars stars stars stars stars stars stars stars stars
Language Go Rust Rust Go Python Java Go Go Go Go Go Go Rust
Dependencies 5 21 73 49 54 16 36 36 58 81 68 116
Lines of code ~4000 ~9500 ~59000 ~148000 ~247000 ~869000 ~5500 ~54000 ~63500 ~330000 ~603000 ~113000 ~239000

Lines of code were calculated using tokei, and last updated on 2024-04-21.

Ory is calculated using Hydra + Oathkeeper + Kratos, per this

obligator's People

Contributors

aeneasr avatar anderspitman avatar newam avatar zhming0 avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

obligator's Issues

Consider adding OpenZiti for application-embedded security

Currently the API is only offered through unix sockets. This reduces the chance that it accidentally gets exposed, which is important because it's not authenticated in any way.

Would you be open to embedding zero trust directly into the project via OpenZiti? OpenZiti allows you to have secure connectivity to the server from anywhere, via a zero trust overlay.

If that sounds interesting, I'd be happy to contribute a patch and if you're into it, demo it over on our YouTube channel too?

Add Impersonation?

It is a pretty important feature for many within the auth / identity space. The only modern provider that seems to have support and documentation for "impersonation" is GoAuthentik - but I've not run through your full comparison table yet. Is that a feature you wouldn't mind adding to your comparison table?
Thanks!

lastlogin.io demo sends emails with invalid magic links

I just followed the instructions at the Demo section to try the obligator instance running at lastlogin.io using my email address. After a little while I recieved the email and clicked on the link but then it goes to lastlogin.io/magic?key...... and the error message "Invalid magic link" is displayed. Hower the URL seems to be fine having a key and an instance_id.

Encrypt all cookies

Don't see a good reason not to, and it offers some defense in depth. At least random apps on the user's machine won't be able to snoop all their logins

TLSAuth parameter for SMTP

Actually testing your interesting software 👍

At the moment it is not possible to use SMTP endpoints which require SSL/TLS encryption for sending mails (e.g. smtp.office365.com) with the docker image. Checked the code and in the struct there are no params for this use case.

It would be nice if this is possible.

Docker repo is 404

pull access denied for anderspitman/obligator, repository does not exist or may require 'docker login'

[feedback] Kanidm comparison table line items

Hi there,

I'm the developer of Kanidm, I wanted to update some of your line items in the readme.

  • Simple - This is subjective, but most of our users would say "yes" to this compared to keycloak or oauth2-proxy.
  • Anonymous - No
  • Multi-domain - No
  • Email Login - No
  • HTTP API - Yes
  • Forward Auth - No (last we looked, it's insecure)
  • Header Auth - Yes
  • OIDC - Yes, we are a full OIDC server
  • SAML - No
  • MFA - Yes, including passkeys and attested passkeys
  • Rev Proxy - No
  • Admin GUI - No
  • Client Registration - No
  • Passkeys - Yes
  • Attested Passkeys - Yes, we are the only IDP that supports these today

We also have active-active replication so we support HA

Fix QR issues

  • Redirect to original instance when clustered
  • Expire codes after a brief time

Using Obligator to protect apps

Currently I am using Authentik to protect my apps. It is too slow and I would like to use Obligator instead. As I understand it, forward auth is the needed feature for this kind of stuff. But I can't wrap my head around what settings I need to specify in my reverse proxy.

I'm using Authentik with Caddy and this setup: https://docs.goauthentik.io/docs/providers/proxy/server_caddy

This way, when someone tries to access an app, he is first redirected to an outpost, where he must login.

Please let me know if this is possible to do with Obligator

suggestion for future consideration: WebAuthN FIDO2 (passkeys)

I have not done much digging into this project yet, but a suggestion I have after reading the readme file is in response to the blurb about sending a unique code to the email. The suggestion is to add a registration flow to confirm ownership of the email like you already are but then also allow linking that email to a FIDO2 token registration via webauthn, which is what passkeys use.

I suggest this because I use a variety of webauthn devices all the time now and I think that method of authenticating is much much better than passwords and is more convenient than clicking on a link sent to your email in my opinion. There are authenticator smartcards (my preference), USB tokens like yubikeys and the opensource derivatives, and of course now google and apple passkeys supported by the trusted platform modules or HSMs on the new phones.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.