DeveloperOffice SSO

Integration guide

Connect any application to one sign-in. Standard OAuth 2.0 and OpenID Connect — point any OIDC client library at us and it works, no custom protocol code.


What you get

Connect an application and your people sign in to it with the same account they use everywhere else — one password, one place to enforce multi-factor, one record of who signed in when. You do not store or check passwords; we do. Your app keeps its own session and its own idea of what a user may do — we simply tell it, at sign-in, who the person is and which roles they hold.

The protocol is ordinary OAuth 2.0 with OpenID Connect on top: authorization-code flow with PKCE, ID tokens signed with ES256, keys published for verification. Any maintained OIDC client library — in any language or framework — speaks it. There is nothing proprietary to learn.


Endpoints

Start from the discovery document. A conforming client library needs only this one URL — it reads every other endpoint, the signing keys, and the supported features from it:

https://login.developeroffice.com/.well-known/openid-configuration

If you would rather configure by hand, these are the paths it returns. All are relative to https://login.developeroffice.com.

Authorization/authorize

Where you send the person to sign in. Authorization-code flow with PKCE.

Token/api/token

Server-to-server exchange of the one-time code for an id_token and access token.

User info/api/userinfo

Profile and roles for a valid access token (Bearer).

JSON Web Key Set/.well-known/jwks.json

Public keys (ES256) for verifying id_token signatures. No shared secret needed.

End session/logout

Sign the person out and, optionally, return them to your app.

ID tokens are signed with ES256; discovery advertises the code response type, the S256 PKCE method, and the openid profile email offline_access scopes. Request offline_access only if you need a refresh token.


Connect your app

1 — Register the application

In the admin console, open Applications and choose Register application. Pick a type:

Web (confidential)client secret

A server-rendered app that can keep a secret. You receive a client ID and a client secret — the secret is shown once, so copy it then.

SPA / native (public)PKCE only

A browser or mobile app that cannot keep a secret. No client secret; PKCE is required and is your protection.

Set your redirect URI — the URL on your app we send the person back to. It is matched exactly: no wildcards, no trailing differences, scheme and path included. Register every URI you will use (staging and production are separate entries). Add a post-logout redirect URI too if you want people returned to your app after signing out.

2 — Store your credentials

Keep these as secrets on your app's host, never in your source tree:

SSO_ISSUER=https://login.developeroffice.com
SSO_CLIENT_ID=do_xxxxxxxxxxxxxxxxxxxxxxxx
SSO_CLIENT_SECRET=...            # web (confidential) clients only

3 — Run the authorization-code flow

Send the person to the authorization endpoint with a PKCE challenge and your own state and nonce values. Keep the PKCE verifier, the state, and the nonce on your side (a short-lived, http-only cookie is the usual choice).

GET https://login.developeroffice.com/authorize
  ?client_id=YOUR_CLIENT_ID
  &redirect_uri=https://your-app.example/auth/callback
  &response_type=code
  &scope=openid%20profile%20email
  &state=RANDOM
  &nonce=RANDOM
  &code_challenge=BASE64URL(SHA256(verifier))
  &code_challenge_method=S256

The person signs in with us — password, TOTP, passkey, or their organization's own Microsoft or Google account. We send them back to your redirect URI with a one-time code, your unmodified state, and an iss value. Check state matches what you stored before you trust anything.

If the person can't or won't complete sign-in, we redirect back with an error parameter instead of a code — most commonly error=access_denied. See error responses below for the full set.

4 — Exchange the code

From your server, exchange the code for tokens. Send your client credentials and the PKCE verifier. The code is single-use and expires within a minute.

POST https://login.developeroffice.com/api/token
Content-Type: application/x-www-form-urlencoded
Authorization: Basic base64(client_id:client_secret)   # web clients

grant_type=authorization_code
&code=THE_CODE
&redirect_uri=https://your-app.example/auth/callback
&code_verifier=THE_VERIFIER

You receive an id_token, an access_token, and — if you asked for offline_access — a refresh_token.

Token lifetimes and refresh rotation are covered in token lifetimes below.

5 — Verify the ID token

Never trust an ID token's contents until you have verified it. Fetch our keys from the JWKS endpoint (cache them) and check the signature, then confirm three things:

Issueriss

Equals https://login.developeroffice.com.

Audienceaud

Equals your client ID.

Noncenonce

Equals the nonce you generated for this sign-in.

Most OIDC libraries do all of this for you when you give them the discovery URL and your client ID.

6 — Identify the person, and read their roles

Match users by the sub claim — a stable, unique identifier for the account that never changes, even if the person changes their email or name. Store sub as the link between your user record and their account. Do not key on email; email can move.

The roles claim carries the roles that person holds in your application's project — the exact role keys you defined in the console (an empty array if they hold none). Map those to whatever rights mean in your app. One account can carry different roles at different applications, so the same person may be an admin in one and a viewer in another.

{
  "sub": "u_a1b2c3d4e5f6...",     // stable — match on this
  "iss": "https://login.developeroffice.com",
  "aud": "do_xxxxxxxx...",
  "nonce": "...",
  "roles": ["admin", "billing"], // your project's role keys
  "urn:devoffice:org": "org_..." // which organization they belong to
}

Profile fields (email, name) arrive in the ID token only if your application opts in; otherwise call /api/userinfo with the access token to read them. The roles claim is returned from both places.

A working relying-party starter (Nuxt) ships with the product at templates/nuxt-rp/ — the same pattern our own sites run in production (see its server/api/auth/sso/ start + callback handlers and server/utils/sso.ts). Any OIDC library in your stack works just as well; the steps above are the whole contract.


Sign-in with Microsoft or Google

An organization can let its people sign in with their existing Microsoft Entra ID or Google Workspace account, rather than a password held here. You add this once, as an org admin, and it applies to everyone in the organization — your registered applications need no changes at all.

In the console, open Identity providers and choose Add provider:

  1. Pick Microsoft (Entra ID) or Google. The endpoints and attribute mapping are pre-filled for you — you supply only your own details.
  2. For Microsoft, enter your tenant (directory) ID so we pin sign-in to your organization. For Google, you may optionally pin a hosted domain.
  3. Register an app on the provider's side and paste its client ID and client secret here. The secret is write-only — stored encrypted, never shown back.
  4. On the provider, set the redirect (reply) URL to the callback shown in the console.
  5. Choose how accounts connect: link to an existing account by verified email, or create accounts automatically on first sign-in. Both are off by default — turn them on deliberately.
  6. Use Test connection, then enable the provider.

A generic OIDC provider works too — supply its issuer and endpoints (Test connection can discover them). The Microsoft Entra walkthrough above is being verified against a live Microsoft tenant; the console steps are authoritative, the exact Microsoft-side labels may differ slightly by portal version.

Sign-in with Microsoft or Google is included on every tier. See pricing.


Directory sync (SCIM)

Keep your directory in step with ours automatically. If your identity platform speaks SCIM 2.0 — Entra ID, Okta, and most others do — it can create, update, and deactivate people and groups here as they change on your side, with no manual re-entry.

Point your platform at this base URL:

https://login.developeroffice.com/scim/v2

In the console, create a SCIM token for your organization (it is shown once — copy it then) and give it to your platform as an OAuth bearer token. We support:

Users/scim/v2/Users

Create, read, update (PUT and PATCH), and deactivate people. Filtering supported.

Groups/scim/v2/Groups

Read group membership.

Discovery/ServiceProviderConfig

Capabilities, plus /ResourceTypes and /Schemas — probed automatically by your platform.

Directory sync is available from the 200-person tier. See pricing.


What each tier unlocks

Sign-in with Microsoft or GoogleEvery tier
Directory sync (SCIM)From the 200-person tier
WebhooksFrom the 200-person tier
SAML and a custom login domainUnlimited tier

Full details on the pricing page.


Getting help

Wiring up your first application, or something not behaving? Write to sam@developeroffice.com — a person will answer.


DeveloperOffice · Colombo, Sri Lanka · sam@developeroffice.com