Light Services

OAuth2 Server

Let third-party applications sign customers in with their store account and read their data.

Overview

Light Store can act as an OAuth2 authorization server. Applications you register get a client ID and secret, send customers to your store to approve access, and receive an access token they can use on the /api/me endpoints.

Typical uses are a "Sign in with your store account" button on another site, a desktop app checking which products a customer owns, or a game server reading a customer's licenses.

Available from 27.2.0

The OAuth2 server ships with Light Store 27.2.0 and is off until you turn it on.

The server implements the authorization code grant (with optional PKCE) and the refresh token grant. The password, implicit and device code grants are not available.

Enabling the server

Open Settings > OAuth2 Server in the admin panel and turn on Enable the OAuth2 server, then save the settings. While it is off, every endpoint on this page answers 404.

The same tab sets the token lifetimes:

SettingDefaultRange
Access token lifetime60 minutes5 to 1440 minutes
Refresh token lifetime30 days1 to 365 days

The key pair signing the tokens is generated on first use and kept encrypted in the database, so it moves with your database backups. Set PASSPORT_PRIVATE_KEY and PASSPORT_PUBLIC_KEY in .env only to sign with a key pair you manage yourself.

Registering an application

In the Applications section of the tab, choose Register application and fill in:

  • Name - shown to customers on the approval screen.
  • Redirect URLs - where customers may be sent back after deciding. A request naming any other URL is refused. Press Enter after each one.
  • Confidential - leave on for applications with a server that can keep a secret. Turn it off for mobile, desktop or single-page apps: they get no secret and must use PKCE.

After saving, the client ID and client secret are shown once. Copy the secret right away: it is stored hashed and cannot be displayed again.

Each application row also offers:

  • Edit - change the name or the redirect URLs.
  • New secret - replaces the secret. The old one stops working immediately.
  • Delete - removes the application and every token it was issued, signing customers out of it.

Endpoints

EndpointMethodPurpose
/oauth2/authorizeGETSends the customer to the store to sign in and approve.
/oauth2/tokenPOSTExchanges an authorization code or a refresh token.
/api/meGETThe customer's profile.
/api/me/purchasesGETThe customer's paid purchases, newest first, 10 per page.
/api/me/licensesGETThe customer's licenses, newest first.

Every parameter and response is listed in the API reference: authorize, token, profile, purchases and licenses.

Scopes

An application asks for scopes in the scope parameter, separated by spaces. The customer sees each requested scope on the approval screen. A request without scope gets profile.

ScopeGrants
profile/api/me: ID, username, avatar and sign-up date.
emailAdds email and email_verified to /api/me.
purchases:read/api/me/purchases.
licenses:read/api/me/licenses.

Authorization code flow

Send the customer to the store. Redirect the browser to /oauth2/authorize with these query parameters:

ParameterValue
client_idThe application's client ID.
redirect_uriOne of the application's redirect URLs, exactly as entered.
response_typecode
scopeSpace separated scopes, for example profile email.
stateA random value your application checks on the way back.
code_challengePKCE: base64url of the SHA-256 of your code verifier.
code_challenge_methodPKCE: S256
https://store.example.com/oauth2/authorize?client_id=9f3c...&redirect_uri=https%3A%2F%2Fapp.example.com%2Fcallback&response_type=code&scope=profile%20email&state=Qx81

Customers who are not signed in sign in first, then see which application asks for what. Once they have approved an application, later requests for the same scopes skip the screen until their tokens expire.

Receive the code. The store redirects back to redirect_uri with code and your state. If the customer cancels, it carries error=access_denied instead. Check that state matches the value you sent.

https://app.example.com/callback?code=def502...&state=Qx81

Exchange the code for tokens. The code is valid for 10 minutes and works once.

curl -X POST https://store.example.com/oauth2/token \
  -H "Accept: application/json" \
  -d grant_type=authorization_code \
  -d client_id=9f3c... \
  -d client_secret=YOUR_SECRET \
  -d redirect_uri=https://app.example.com/callback \
  -d code=def502... \
  -d code_verifier=YOUR_PKCE_VERIFIER

Public applications leave out client_secret and must send code_verifier. The response holds the tokens:

{
	"token_type": "Bearer",
	"expires_in": 3600,
	"access_token": "eyJ0eXAiOiJKV1Qi...",
	"refresh_token": "def50200a1b2..."
}

Call the API with the access token as a Bearer token:

curl https://store.example.com/api/me \
  -H "Accept: application/json" \
  -H "Authorization: Bearer eyJ0eXAiOiJKV1Qi..."
{
	"data": {
		"id": 42,
		"name": "jane",
		"avatar_url": "https://store.example.com/storage/avatars/42.webp",
		"created_at": "2026-03-14T09:26:53.000000Z",
		"email": "jane@example.com",
		"email_verified": true
	}
}

Refreshing tokens

When the access token expires, trade the refresh token for a new pair. Each refresh token works once: store the new one from the response.

curl -X POST https://store.example.com/oauth2/token \
  -H "Accept: application/json" \
  -d grant_type=refresh_token \
  -d client_id=9f3c... \
  -d client_secret=YOUR_SECRET \
  -d refresh_token=def50200a1b2...

Revoking access

Customers see every application they approved under Account > Connected Apps in their profile, with what it can read, and can Revoke access there. Revoking ends every access and refresh token the application holds for that customer at once: its next request answers 401, and its next sign-in shows the approval screen again.

Deleting an application in the admin panel revokes it for every customer.

API endpoints

The /api/me endpoints accept only OAuth2 access tokens; API keys from the admin panel do not work there, and access tokens do not work on the other API routes. Their responses are documented in the API reference.

Send Accept: application/json with every request so errors come back as JSON:

StatusMeaning
401The access token is missing, expired, revoked or invalid.
403The token was not granted the scope this endpoint needs.
404The OAuth2 server is turned off in the settings.

On this page