Developers
Add Harbor sign-in to your app
Use the OpenID Connect (OIDC) client your app already has. Register its callback in Harbor, copy the issuer and client ID, and use Authorization Code with PKCE. New Harbor apps have no client secret.
Set up your app
- 1. Register your callback. After business signup, open App setup and register your own app for Harbor sign-in. Enter its exact callback URL. The form suggests
http://127.0.0.1:3000/callbackfor local testing; replace it if your app uses another route. - 2. Copy your settings. Your app page shows the issuer, client ID, and registered callback. Put those values in your app’s OIDC configuration. You do not need an API key or client secret for user sign-in.
- 3. Test sign-in. Send the browser to Harbor from your app’s sign-in route. Harbor returns it to your callback. Validate the response, identify the user by the returned
sub, and create a session in your app.
If your app already supports OIDC
Add Harbor as an identity provider with these settings. Your OIDC library can discover Harbor’s authorize, token, and signing-key endpoints from the issuer.
| Setting | Use |
|---|---|
| Issuer | Copy HARBOR_ISSUER from your app page. Enable OIDC discovery. |
| Client ID | Copy HARBOR_CLIENT_ID from your app page. |
| Redirect URI | Use the exact callback registered in Harbor. |
| Flow and scope | Authorization Code, PKCE with S256, and openid scope. |
| Client authentication | none for new apps. Leave the client secret empty. |
Have separate local and production callbacks? Add both to the same Harbor application, then select the callback that matches your environment.
Adding OIDC to a Node app
These functions fit into your own sign-in and callback routes. They use openid-client v6 and rely on your app’s session store.
Show the Node integration example
npm install openid-client@^6.8.7Set HARBOR_ISSUER, HARBOR_CLIENT_ID, and HARBOR_REDIRECT_URI from your app page before using this code.
import * as oidc from 'openid-client';
const redirectUri = process.env.HARBOR_REDIRECT_URI;
const harbor = await oidc.discovery(
new URL(process.env.HARBOR_ISSUER),
process.env.HARBOR_CLIENT_ID,
undefined, oidc.None(),
{ execute: [oidc.enableNonRepudiationChecks] },
);
// Call from your existing sign-in route. Save the session before redirecting.
export async function startHarborSignIn(session) {
const verifier = oidc.randomPKCECodeVerifier();
const challenge = await oidc.calculatePKCECodeChallenge(verifier);
const state = oidc.randomState();
const nonce = oidc.randomNonce();
session.harborSignIn = { verifier, state, nonce };
return oidc.buildAuthorizationUrl(harbor, {
redirect_uri: redirectUri, scope: 'openid',
code_challenge: challenge, code_challenge_method: 'S256',
state, nonce,
}).href;
}
// Call from your registered callback route with its full URL.
export async function finishHarborSignIn(session, callbackUrl) {
const pending = session.harborSignIn;
if (!pending) throw new Error('Sign-in expired. Start again.');
delete session.harborSignIn;
const tokens = await oidc.authorizationCodeGrant(
harbor, new URL(callbackUrl), {
pkceCodeVerifier: pending.verifier,
expectedState: pending.state,
expectedNonce: pending.nonce,
idTokenExpected: true,
},
);
const subject = tokens.claims()?.sub;
if (!subject) throw new Error('Harbor did not return a subject.');
return subject; // Look up or create your user, then start your app session.
}Keep the PKCE verifier, state, and nonce in a short-lived server-side login transaction tied to the browser. Save it before redirecting and clear it when the callback is handled. Use your own session store for the signed-in user; do not put tokens or the verifier in browser storage.
What your users see
Business signup sets up your Harbor account and app registration. It is separate from the sign-in flow your users see.
A user starts in your app, goes to Harbor to create a personal account or sign in to an existing one, then returns to your callback. Your app decides what that signed-in user can do. Harbor’s sub is specific to your app; use it as the Harbor account key rather than assuming an email address identifies the user.
The demo shows the user journey. Your integration runs in your own app.
Before you go live
- Add your production HTTPS callback to the same Harbor application. Callback URLs must match exactly; wildcards are not accepted.
- Test sign-in with a new Harbor user and a returning user.
- Use a shared, durable session store if your app runs on more than one server.
If sign-in fails
| Check | What to do |
|---|---|
| Callback mismatch | Compare your app’s redirect URI to the one in Harbor, including scheme, port, path, and trailing slash. |
| Client secret requested | Select a public client or none token-endpoint authentication for a new Harbor app. |
| State or PKCE error | Make sure the same browser session can retrieve the verifier, state, and nonce after Harbor redirects back. |
Using SAML instead? Contact us. For OIDC endpoints and supported settings, view Harbor’s discovery document.