How to implement Promise on your site

Promise uses a simplified version of the Implicit Flow in the protocol OpenID Connect (OIDC).

The following guide uses example.com as client_id. Your client_id should be the shortest possible domain name you control.

Sign in users

To sign in users, redirect them to

https://promiseauthentication.org/a/example.com?nonce=f67c2cee

The nonce is a randomly generated string that enables protection against replay attacks.

Please notice that the client_id is part of the URI. The other Authentication Request parameters are supplied as URL query parameters. Eg. ?nonce=abc.

Now, Promise will take care of authentication and redirect the user back to

https://example.com/authenticate?id_token=eyJhbGc...

The id_token will be a JWT looking like this:

{
  "jti": "32e98538-264d-412f-b763-950af1d15519",
  "sub": "unique-user-id",
  "aud": "example.com",
  "iss": "https://promiseauthentication.org",
  "iat": 1714108533,
  "nonce": "f67c2cee"
}

When receiving the id_token, you have to

  1. Validate that the nonce is not replayed.
  2. validate that the token actually came from Promise, by using the public key available at
    https://promiseauthentication.org/.well-known/jwks.json

You can do that by using your preferred JWT library. Find yours here.

When you have validated the JWT, you must identify the user by concatenating both iss and sub. For example:

https://promiseauthentication.org|unique-user-id

This is important as this enables Promise in the future to delegate the responsibility of authentication to a provider chosen by the user.

Please notice that the users e-mail is not included in the JWT. This is by design. If you need an e-mail, or any other personal data belonging to the user, you will have to get that yourself by asking nicely.

Authentication Request

The Authentication Request used in the implementation on Promise differs a bit from the general specification of the Authentication Request and the specification of Authentication Requests specifically for the Implicit Flow.

The parameters scope and response_type are ignored as they do not convey anything meaningful in the implementation used at Promise. response_type is always "id_token".

client_id
REQUIRED
This is the domain. Be advised, that the same user will get a different user identifier (sub) for each client_id. So you should pick your client_id to be the shortest possible domain you control.
Example: example.com
redirect_uri
OPTIONAL but REQUIRED in specification
If you want to redirect the user to anywhere else than the default provide the URI here. You will have to whitelist the redirect_uri in the allowed_redirect_domain_names in the configuration.
Default: "https://example.com/authenticate"
nonce
OPTIONAL but REQUIRED in specification
As Promise also includes a jti in the generated token, requiring a nonce is not the only possible way to mitigate replay attacks.

The following parameters is not implemented yet: max_age, ui_locales, login_hint, acr_values.

The following parameters will probably never be implemented: response_mode, display, prompt, id_token_hint.

Configuration of Promise

Promise can be configured by example.com by providing a JSON object on

https://example.com/.well-known/promise.json

Configurable attributes

admin_user_ids (Array<String>)
An array of user IDs you want to have admin access here on Promise.
Example: ["a", "b"]
allowed_redirect_domain_names (Array<String>)
A list of domain names that Promise will allow as redirect_uri. This list does not have to include the default redirect_uri. Also be aware that localhost and 127.0.0.1 is allowed by default, so no need to add that. You can also provide IP addresses.
Example: ["a.example.com"]
locale (String)
The locale preferred, if the user has not chosen a locale. Currently "da" and "en" are supported.
Example: "da"
logo_url (String)
An URL for a logo. Will be shown to users on sign in screens. The logo will be downloaded on the server and sent to the client as Base64 encoded data. This is to protect the user from potential tracking.
Example: "https://example.com/logo.png"
name (String)
The name of the app or service. Will be shown on login screens.
Example: "Nice app"

Current configuration for example.com

When fetching from

https://example.com/.well-known/promise.json

we get

{
}

Promise caches the JSON based on the HTTP headers:

{
  "cache-control": "max-age=604800",
  "content-type": "text/html; charset=UTF-8",
  "date": "Fri, 26 Apr 2024 05:15:34 GMT",
  "expires": "Fri, 03 May 2024 05:15:34 GMT",
  "server": "EOS (vny/0451)",
  "content-length": "433"
}

The configuration is needed everytime we show a sign in screen for example.com to a user, so allowing Promise to cache it seems sensible. Suggested headers include Etag and Cache-Control: public, must-revalidate, max-age=300. But that would be your call.

Admin

If you want to become an admin for example.com, you can add your Promise user ID to the example.com configuration of Promise:

{
  …
  admin_user_ids": [
    "your-user-id-on-promise"
  ],
  …
}

✨ That's it (for now)! ✨


Sign in
English Dansk