auth/

directory
v0.19.1237 Latest Latest
Warning

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

Go to latest
Published: Oct 9, 2026 License: AGPL-3.0

README

Auth Service

This is technically a full stack app. We are building it to conform to the way we have built the other HTTP services, which are all APIs, to maintain our patterns. At a future date, we will refactor our fx deps and the cmds. For now, we respect convention.

Prior Art

This service borrows heavily from https://github.com/vouch/vouch-proxy.

Configs

New env vars for auth.nuon.co

Env var Description Scope
ROOT_DOMAIN The root domain for all the services. e.g. byoc.org.co. All Services
NUON_AUTH_SESSION_KEY Session Key for nonce Auth Service
NUON_AUTH_ALLOWED_DOMAINS Domains from which any user can register. Usually the org email. Auth Service
NUON_AUTH_ALLOW_ALL_USERS Whether or not any user with an email from an allowed domain should be allowed to register. Auth Service

We construct a domain from the root domain by prefixing auth to it. e.g. app.byoc.org.co. This becomes the service domain which the service understands itself to be available at .

[!IMPORTANT] These values are required. If they do are not present, the service will not start.

[!NOTE] NUON_AUTH_ALLOW_ALL_USERS defaults to false.

[!NOTE] NUON_AUTH_ALLOWED_DOMAINS cannot be empty.

Identity Providers

This service depends on a the table identity_providers and on the default identity provider configured via env vars.

Configs

There are two sources for configurations.

  1. Env vars: a default provider must be provided via env vars. If these configs are not provided, the service will not start up.
  2. Database: additionaly IdentityProviders can be created. These store the configs in a json column.
Env Var Configs
Env var Description
nuon_auth_provider_type one of 'oidc', 'google', 'github'
nuon_auth_client_id Client ID
nuon_auth_client_secret Client Secret
nuon_auth_issuer_url Issuer URL
nuon_auth_redirect_url Redirect URL
nuon_auth_provider_name Optional label shown on the sign-in page. Defaults to a type-derived
name ('Single Sign-On', 'Google', 'GitHub').
Examples

For these examples, assume the BYOC Nuon is deployed with the following ROOT_DOMAIN: byoc.org.co.

Auth0 as a generic OIDC provider

Env var Value
nuon_auth_provider_type 'oidc'
nuon_auth_client_id [secret]
nuon_auth_client_secret [secret]
nuon_auth_issuer_url https://audience.auth0.com
nuon_auth_redirect_url auth.byoc.org.co/auth

Google as an OAuth provider

Env var Value
nuon_auth_provider_type 'google'
nuon_auth_client_id [secret]
nuon_auth_client_secret [secret]
nuon_auth_redirect_url auth.byoc.org.co/auth
in-database configs

If additional IdPs must be created, we need to add to the identity_providers table. At the time of writing, only install-wide configs are supported. This means any providers added this way will be available to all users of the install. As such, these are created via and admin api endpoint.

Future: in the future we'll add support for org-specific IdPs. At that point, an org-specific endpont on the public api may be added.

Examples

Auth0 as a generic OIDC provider

{
  "enabled": true,
  "name": "Acme SSO",
  "allow_all_users": false,
  "provider_type": "oidc",
  "openid_config": {
    "client_id": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "client_secret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
    "issuer_url": "https://audience.auth0.com",
    "redirect_url": "http://auth.byoc.org.co/auth"
  }
}

Github

{
  "enabled": true,
  "provider_type": "github",
  "github_config": {
    "client_id": "XXXXXXXXXXXXXXXXXXXX",
    "allowed_orgs": ["999999999"],
    "redirect_url": "https://auth.byoc.org.co/auth",
    "allowed_teams": ["1111111"],
    "client_secret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx"
  }
}

Default scopes are, user:email and read:user.

[!IMPORTANT] If a user does not have a public email, they will not be able to authenticate.

[!IMPORTANT] If a user's primary email does not have an allowed domain, they will not be able to authenticate.

Loading Order

The default provider is required for startup. this default provider is composed from the provider configs loaded from the env files.

After startup, we load the rest of the providers with a helper called getIdentityProviders. This helper loads the default provider from the env and then loads any additional providers from the database, oldest first. That ordering is the sign-in page's button order, so the env provider is always the first button.

Providers are identified by their ID. The env provider has no database row, so it gets the synthetic ID default-<provider_type> (app.EnvIdentityProviderID); database providers use their row ID. /login?provider=<id> still accepts a bare provider type for links minted before providers became individually addressable, which resolves to the first enabled provider of that type.

Provider Lookup
Function Purpose
getIdentityProvider(ctx, ref) Main entry point - resolves an ID, falling back to a bare type
getIdentityProviderByType(ctx, ProviderType) Looks up the first IdentityProvider of a type (env first, then DB)
getDefaultIdentityProvider() Builds IdentityProvider from env vars only
createProviderFromIdentityProvider(ip) Converts IdentityProvider model to Provider interface
Uniqueness

Any number of providers of a given type can be configured: one through env vars, the rest in the database. Two OIDC providers (say Auth0 for staff and Entra for contractors) is the case this exists for. Registering the same application twice - same type, client ID and, for OIDC, issuer - is rejected with a 409.

Because several providers can share a type, provider_type no longer identifies a provider. account_identities is keyed on (identity_provider_id, sub) and (account_id, identity_provider_id) instead, and identity_provider_id carries the env sentinel for accounts that signed in through the env provider. It is deliberately not a foreign key: the env provider has no row, and ON DELETE SET NULL would collapse a deleted provider's identities onto each other.

nuon_auth_allow_all_users is deployment-wide, which cannot express "staff IdP open to self-signup, contractor IdP invite-only". A provider may set allow_all_users to override it; leaving it unset keeps the deployment-wide behaviour.

Creating or updating a DB-configured OIDC provider runs discovery against the issuer and rejects the request if it fails. Env providers get that check at boot; without this a typo in the issuer surfaced as a 500 on someone's first login rather than an error for the admin who typed it.

Give each provider a name when there is more than one. Without one, the sign-in page falls back to a type-derived label, and for OIDC it shows the issuer host underneath so two unnamed OIDC buttons are still distinguishable.

Internals

Templates

This is the only service that is using templates. Even then, all templates are "namespaced" with auth so they are all registered with this naming convention auth/*.tmpl. The files are all named *.html.tmpl.

Middleware

This service does need or know about the Org or Auth middleware in use by the other API services. This service has only a few endpoints so it doesn't actually implement an auth middleware for its own use and instead relies on the session.

Session

The auth service maintains a short lived session, nuon-auth-session, to store the next url (final destination) and the provider type. This session is deleted as soon as the auth flow is complete and is only ever available to the auth service itself. The service uses the NUON_AUTH_SESSION_KEY to sign the cookie for the session (HMAC-SHA256).

When auth is complete, the auth provider sets a cross domain cookie X-Nuon-Auth that is accessible to any service on ROOT_DOMAIN (e.g. byoc.org.co). This cookie is invalidated when a user visits /logout.

Forthcoming

SAML

SAML support will be built into this service.

SCIM

SCIM support will be built into this service.

Runner Auth

We have a method to identify the runner via a presigned sts get caller identity call.

  1. Runner creates a presigned STS GetCallerIdentity request (doesn't send it)
  2. Runner sends the presigned request details to your service
  3. Your service makes the actual STS call using those headers
  4. AWS validates the signature and returns the caller identity
  5. Your service validates the identity matches expected runner
Additional Claims

We'd like to add support for additional claims so the IdP can provide additional details about the user. This is a future-looking feature leaving room for the concept of roles and org access.

attr desc
groups list of strings where each string is a group such as admin or developer
orgs list of strings where each string is, ideally, an org id or an org slug

Caveats & Limitations

Changing a Provider of a Given Type

Changing an env-var provider to a different provider of the same type is likely to cause issues. At this time, this is not explicitly supported. This applies to both, env-var based IdPs and in-database IdPs. Adding a second provider instead of repointing the first one avoids this entirely.

Preflight

nctl preflight validates the env-configured provider, including real OIDC discovery against the issuer. It cannot see database-configured providers at all.

CLI Flow

To login via the CLI:

nuon auth login

This will pull down the cli configs from the api. If Nuon Auth is enabled, it will use the auth service for authentication.

Once the flow starts, the CLI will open the /device/code endpoint. If the user is not authenticated, the endpoint will redirect the user to the index page with the s.cfg.Domain + /device/code as the url param. If the user is authenticated, the device code flow will start.

[!NOTE] The /device/code endpoint should be opened directly, otherwise the user may invalidate their session for the browser.

 Flow

 CLI                                 Browser                              Server
  |                                     |                                      |
  |-- Generate device_code locally ---->|                                      |
  |-- Open browser /device/code?code=XXX ------------------------------------->|
  |                                     |<-- Check X-Nuon-Auth cookie ---------|
  |                                     |<-- Show "Approve CLI?" page ---------|
  |                                     |-- User clicks Approve (POST) ------->|
  |                                     |            (stores code → account_id)
  |                                     |<-- Show success page ----------------|
  |-- Poll GET /device/token?code=XXX ---------------------------------------->|
  |<-- 200 {access_token: "tok..."} -------------------------------------------|

Directories

Path Synopsis

Jump to

Keyboard shortcuts

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