Integrate a confidential client with generative AI Developer

Last updated Aug 15, 2026

Integrate a confidential client with generative AI

This page is for developers who want generative AI to integrate confidential-client OAuth 2.1 / OpenID Connect (OIDC) sign-in. You can complete a basic integration by following the steps even if you have not studied the protocols in depth.

There are four steps:

  1. Give the prompt on this page to the AI.
  2. Register the Redirect URI the AI outputs in the Swaymoon Developer identifier wizard (choose Confidential client).
  3. Set the portal-issued client_id and client_secret as server environment variables.
  4. Start the app and test the Sign in with Swaymoon Account button.

The prompt on this page targets apps that have a server runtime that can keep secrets. If the project ships as pure HTML, a static site, or browser-only, read Integrate a public client with generative AI and Minimal Python example instead. Do not invent a client_secret.

If you are unsure about the architecture, the prompt asks the AI to check first: is there a traditional backend, API Route, Serverless Function, or BFF (Backend for Frontend) that can store client_secret safely?

Critical security

client_secret authenticates a confidential client. Keep it on the server only. Do not send it to the AI, and do not put it in frontend code, client-visible environment variables, or a Git repository. Public clients are not issued a client_secret.

Step 1: Give the prompt to the AI

Open the project in an AI coding tool, then copy the entire prompt below.

Do not add real credentials to the prompt. The AI should implement the code first, then output the Redirect URI to register.

Integrate “Sign in with Swaymoon Account” in the current project. Apply the code changes and run any tests that are needed.

Interaction goals:
- Add a “Sign in with Swaymoon Account” button on a suitable sign-in page.
- After the user chooses the button, they go to the official Swaymoon page to sign in.
- After success they return to this app and stay signed in.

Check the project’s deploy and runtime shape first:
- If the project has a server, API Route, Serverless Function, or BFF that can keep client_secret safely, continue as a confidential client.
- If the project is pure HTML, a static site, or a pure SPA where token exchange can only happen in the browser: do not use a confidential client. Use a portal public client instead (clientType=public, token endpoint auth none) and implement PKCE token exchange with no client_secret (see “Integrate a public client with generative AI” and “Minimal Python example”).
- Never put client_secret in the browser or frontend code.

Integration settings:
- Issuer: https://api-passport.swaymoon.com
- Discovery: https://api-passport.swaymoon.com/.well-known/openid-configuration
- Flow: Authorization Code + PKCE (S256)
- With a server, client auth: client_secret_basic (confidential client). Frontend-only: none (public client; form includes client_id + code_verifier; no refresh_token)
- ID Token signing algorithm: ES256
- Prefer sign-in entry: GET /auth/swaymoon
- Prefer callback entry: GET /auth/swaymoon/callback
- Request openid only by default. Add email or specific profile fields (such as name, picture) only if this product truly needs them. Do not take a bundled profile.

Implementation requirements:
1. Prefer a maintained OIDC client library that fits the current framework, and take protocol endpoints from Discovery.
2. Generate and store state, nonce, and PKCE code_verifier on the server; validate them strictly on callback.
3. Exchange tokens using only SWAYMOON_CLIENT_ID and SWAYMOON_CLIENT_SECRET on the server.
4. Verify the ID Token ES256 signature with JWKS, and verify iss, aud, exp, iat, and nonce.
5. Call UserInfo on the server and confirm its sub matches the ID Token sub.
6. Look up or create a local user with sub. Do not use email as the user primary key. Interpret profile fields from the token scope; ungranted items are default placeholders, not real data.
7. Create this app’s own secure sign-in session. The browser only stores an app session Cookie with HttpOnly and a sensible SameSite. Do not store Swaymoon tokens in the browser. In production over HTTPS, Secure is required.
8. Post-sign-in redirects may only be in-site relative paths or a trusted allowlist, to avoid open redirects.
9. Do not store secrets or tokens in localStorage, sessionStorage, ordinary cookies, logs, error pages, or test snapshots.
10. Do not store secrets in public variable prefixes such as NEXT_PUBLIC_, VITE_, NUXT_PUBLIC_, PUBLIC_.
11. Create a .env.example with no real values, and make sure local secret files are ignored by .gitignore.
12. Do not ask me to send a real client_secret in the chat.

When you finish, give clear, actionable integration notes:
1. “Copy this entire line into the Swaymoon Developer portal”, then print the full Redirect URI for local development on its own line.
2. The full Redirect URI to register for production. If the production host is unknown, mark the parts that must be replaced.
3. Which environment variables I must set on the server, and where to fill them in the current framework or deploy platform. Show names only; do not show or guess secret values.
4. How to start the project, find the sign-in button, and complete one test.
5. Which files you changed.

After the AI changes the code, find output like this:

Copy this entire line into the Swaymoon Developer portal:
http://127.0.0.1:3000/auth/swaymoon/callback

The address above is an example. Use the port and path the AI outputs for the current project.

Step 2: Register the Redirect URI

What is a Redirect URI?

Redirect URI is Redirect URI in the Developer portal wizard: a callback endpoint registered on the OAuth client in advance. After the user authenticates and confirms authorization on Swaymoon Account, the authorization server redirects the browser here with the authorization response, such as code and state.

The Redirect URI usually maps to a callback route the AI created on your backend, not a normal home page. That route validates the authorization response, exchanges tokens, creates an app session, and redirects the user to the post-sign-in page.

A Redirect URI is usually app Origin plus callback path. Origin includes scheme, host, and port, for example https://app.example.com or http://127.0.0.1:3000.

App Origin + /auth/swaymoon/callback

Examples:

ScenarioApp addressRedirect URI
Local developmenthttp://127.0.0.1:3000http://127.0.0.1:3000/auth/swaymoon/callback
Production sitehttps://app.example.comhttps://app.example.com/auth/swaymoon/callback

Prefer the full Redirect URI the AI outputs from the project’s routes and listen port. Do not guess.

Exact-match rules

redirect_uri in the authorization request must match the registered portal value character for character. This check prevents authorization codes from being sent to unregistered addresses. These differences count as different URIs:

  • http:// versus https://;
  • 127.0.0.1 versus localhost;
  • port 3000 versus 3001;
  • a trailing / may also differ.

Copy the AI’s URI in full. Do not edit it by hand. If the AI outputs local and production URIs separately, register each in the Developer portal.

Fill in the Developer portal

  1. Sign in to the Swaymoon Developer portal.
  2. Open Identifiers and click + to start Register an app identifier.
  3. Description: a name users will recognize on the consent page. With a server, choose Confidential client.
  4. Icon: use the default, or upload, crop, and continue.
  5. Redirect URI: paste the full Redirect URI from the AI (add local and production separately if needed).
  6. Capabilities: keep openid required. Set email only if you need a contact address (required / optional / not requested). Profile fields can be optional or required (required needs a reason and privacy policy). The user may choose Hide My Email; treat it as a normal email. See Scopes and consent.
  7. After Confirm, click Register.

On success the portal shows client credentials (save them immediately):

  • client_id: public OAuth client identifier for the app that starts authorization;
  • client_secret: confidential-client credential, shown only at creation. Save it securely at once (public clients do not have this field).

Step 3: Configure client credentials

The AI has usually already created a template like this:

SWAYMOON_ISSUER=https://api-passport.swaymoon.com
SWAYMOON_CLIENT_ID=
SWAYMOON_CLIENT_SECRET=
SWAYMOON_REDIRECT_URI=

Follow the AI’s notes for your framework and put real values in server environment variables.

  • Locally, that is usually .env.local or similar, already ignored by .gitignore.
  • For a deployed site, use the platform’s Environment Variables, Secrets, or equivalent settings page.
  • SWAYMOON_REDIRECT_URI is the full Redirect URI you just registered.
  • If the AI also asks for an app session secret, generate a random value with the command it gives in your local terminal, then fill the environment variable yourself. Do not send the generated value back to the AI.

Credential security

All of the following are forbidden:

  • Do not paste client_secret into the AI chat;
  • Do not put client_secret in HTML, React, Vue, or other frontend files;
  • Do not store it in frontend-public variables such as NEXT_PUBLIC_, VITE_, NUXT_PUBLIC_, PUBLIC_;
  • Do not commit a .env file that contains real secrets to Git;
  • Do not store Access Token, ID Token, or Refresh Token in localStorage.

If the AI wants client_secret in the frontend, stop using that implementation and have the server or BFF do the token exchange instead.

Step 4: Test sign-in

  1. Start the project with the command the AI gave you.
  2. Open the page that has the sign-in button.
  3. Choose Sign in with Swaymoon Account.
  4. Complete sign-in and authorization on the official Swaymoon page.
  5. The browser returns to your app and shows a signed-in state.

Users only need one sign-in button. Authorization redirect, callback checks, token exchange, and app-session creation all happen on the server.

<a href="/auth/swaymoon">Sign in with Swaymoon Account</a>

The AI generates the real button component and styles for the current project. You do not need to copy the HTML above by hand.

FAQ

You can give error text to the AI for help, but strip client credentials, tokens, and full callback query strings first. Do not send a full browser URL that contains code= or state=.

ProblemHow to check
Redirect URI mismatchCopy the AI’s full URI again and compare it character by character with the portal registration
invalid_clientCheck that client_id and client_secret are in server environment variables, then restart the project
After authorization, the app never returnsCheck that the Redirect URI for the current environment is registered in the portal
AI detected a frontend-only / static architectureSwitch to Integrate a public client with generative AI and Minimal Python example, or add a server that can keep secrets and continue on this page
UserInfo has no emailToken lacks email (user unchecked or it was not requested). When granted, the field should always be present. Check scope in the token response
Avatar / nickname does not look realWithout the matching field scopes, UserInfo still has fields, but they are default placeholders (see Tokens and user info). Use scope to tell real data
email looks like a privacy relayThe user chose Hide My Email on the consent page. Treat it as a normal email; do not ask for the real address

For the full protocol and parameters, read Integration overview, Authorization code and PKCE, Tokens and user info, Scopes and consent, Examples and troubleshooting, and Minimal Python example. Client registration is Registration and configuration in this directory.