Developers
Sign in with JSON Resume
Let people bring their resume to your HR tool, job board or applicant tracker. They sign in with jsonresume.org and choose what you may see; you get their resume.json, kept up to date by them, with no forms to fill in again.
// auth.ts: Auth.js (NextAuth) with JSON Resume as a provider
providers: [
{
id: 'jsonresume',
name: 'JSON Resume',
type: 'oauth',
authorization: {
url: 'https://jsonresume.org/oauth/authorize',
params: { scope: 'profile email resume:read' },
},
token: 'https://jsonresume.org/api/oauth/token',
userinfo: 'https://jsonresume.org/api/oauth/userinfo',
clientId: process.env.JSONRESUME_CLIENT_ID,
clientSecret: process.env.JSONRESUME_CLIENT_SECRET,
checks: ['pkce', 'state'],
profile: (p) => ({
id: p.sub,
name: p.name ?? p.preferred_username,
email: p.email,
image: p.picture,
}),
},
],OAuth 2.1
How it works
Register your app
Under Settings → Your OAuth apps, give its name and the URLs people come back to. You get a client ID and a secret, shown once. Keep the secret on your server.Send people to jsonresume.org
Link to the authorization page with the scopes you need and a randomstate. They sign in (or sign up), see exactly what you asked for, and choose Allow.https://jsonresume.org/oauth/authorize?response_type=code &client_id=$CLIENT_ID &redirect_uri=https%3A%2F%2Facme.example%2Fauth%2Fjsonresume%2Fcallback &scope=profile%20email%20resume:read &state=$STATESwap the code for tokens
They come back to your redirect URI withcodeand yourstate. From your server, exchange the code within 10 minutes. Access tokens last an hour; refresh tokens 90 days, and each is used once.curl https://jsonresume.org/api/oauth/token -u "$CLIENT_ID:$CLIENT_SECRET" \ -d grant_type=authorization_code \ -d code=$CODE \ -d redirect_uri=https://acme.example/auth/jsonresume/callbackRead their resume
Send the access token as a Bearer token. Who they are is at/api/oauth/userinfo; their resume, as JSON Resume, at/api/oauth/resume.curl https://jsonresume.org/api/oauth/resume -H "Authorization: Bearer $ACCESS_TOKEN"
Ask for what you need
Scopes
| Scope | Lets your app |
|---|---|
profile | Their username, name, photo and page, from /api/oauth/userinfo. What you get when you ask for nothing. |
email | Their email address, and whether it is verified. |
resume:read | Their resume as JSON Resume, from GET /api/oauth/resume. |
resume:write | Replace their resume with PUT /api/oauth/resume. Saved to their gist when their page reads from it, and checked against the schema. |
mcp | Everything, through the MCP server. For agents, not HR tools. |
Reference
Endpoints
| Authorization | https://jsonresume.org/oauth/authorize |
| Token | POST https://jsonresume.org/api/oauth/token |
| User info | GET https://jsonresume.org/api/oauth/userinfo |
| Resume | GET, PUT https://jsonresume.org/api/oauth/resume |
| Revocation | POST https://jsonresume.org/api/oauth/revoke |
| Metadata (RFC 8414) | https://jsonresume.org/.well-known/oauth-authorization-server |
Details
Good to know
- Use
subas the user’s ID: usernames can change. - PKCE (S256) is optional for apps with a secret and required for apps without one. Browser and mobile apps without a server can register themselves (RFC 7591,
POST /api/oauth/register) and must use PKCE. - People see every app they’ve connected under Settings → Connected apps, and can disconnect it there. Revoke a token yourself when someone disconnects on your side.
- Errors follow RFC 6749 and 6750:
invalid_grant,invalid_client,invalid_token(401) andinsufficient_scope(403).