Skip to main content
Shipyard uses GitHub OAuth to identify you — there are no passwords or API keys to manage. When you complete the OAuth flow, Shipyard issues a short-lived JWT that you include in every API request. The GitHub access token stays on the server; you only handle the JWT.

OAuth flow

1

Redirect the user to GitHub

Send the user to GET /api/auth/github. The server immediately redirects to GitHub’s OAuth authorization page with the required scopes.
GET /api/auth/github
2

GitHub redirects back with a code

After the user grants access, GitHub redirects to /api/auth/github/callback with a code query parameter. This is handled automatically — you don’t need to call this endpoint yourself.
3

Shipyard exchanges the code and upserts the user

The callback handler exchanges the code for a GitHub access token, fetches the user’s profile (and email if the primary email is hidden), then creates or updates the user record. It issues a signed JWT and redirects to:
<FRONTEND_URL>/auth/callback?token=<jwt>&username=...&avatar=...&email=...&createdAt=...
4

Extract and store the JWT

Parse the token query parameter from the redirect URL and store it in your application (for example, in localStorage or a secure cookie). You’ll use this token on every subsequent request.

Using the JWT token

Include the token in the Authorization header on all protected API requests:
curl https://your-server/api/project/projects \
  -H "Authorization: Bearer <your-jwt-token>"
Tokens expire after 1 hour. When the token expires, the server responds with 401 and you must restart the OAuth flow to get a fresh token.

WebSocket authentication

Real-time build logs are delivered over Socket.io, which uses a separate authentication path. Pass the JWT in the auth.token field when opening the connection:
const socket = io("http://localhost:8080", {
  auth: { token: "<your-jwt-token>" }
});
Shipyard’s Socket.io middleware verifies the token before allowing the connection. If the token is missing or invalid, the connection is rejected immediately.

GitHub OAuth scopes

Shipyard requests three scopes when redirecting to GitHub:
ScopePurpose
read:userFetch your username, avatar, and email
repoClone private repositories and register webhooks via the GitHub API
read:orgList your organization memberships and their repositories

Authentication errors

ScenarioBehavior
Missing Authorization header401 Not Authenticated
Malformed or expired JWT401 Token expired or invalid
Invalid Socket.io tokenConnection rejected with Authentication error
The GitHub access token used to clone repos and call the GitHub API is stored server-side and refreshed each time you sign in. Your client application only ever sees the Shipyard JWT.

Build docs developers (and LLMs) love