Skip to content

OpenID Connect Protocol ​

StarEcho implements the standard OpenID Connect 1.0 protocol (based on OAuth 2.0 Authorization Code Flow), enabling third-party applications to integrate with the StarEcho account system for "Sign in with StarEcho" functionality.

Overview ​

ItemDescription
ProtocolOpenID Connect 1.0 (Authorization Code Flow)
Signing AlgorithmRS256
Supported Scopesopenid, profile, email
Supported Grant Typesauthorization_code, refresh_token
Client Authenticationclient_secret_post, client_secret_basic, PKCE
Discovery Endpointhttps://api.starecho.net/.well-known/openid-configuration

Authorization Code Flow Sequence Diagram ​

Integration Guide ​

1. Create an Application ​

Sign in to StarEcho, navigate to Profile → Developer → Developer Apps, and click "Create App":

FieldDescription
App NameName displayed to users on the authorization page
App DescriptionBrief description of your application
Account UsageExplain how your app will use StarEcho accounts
Redirect URIsOAuth redirect URI after authorization, must be https://
Web Origins (CORS)Exact origins allowed to call StarEcho APIs from a browser, for example https://app.example.com, without a path
Mobile URL SchemesOptional, for native mobile app callbacks, e.g. starecho, full callback: starecho://callback
Homepage URLOptional, your application's website
App IconOptional, JPG/PNG, 256×256 recommended

Desktop Loopback: http://127.0.0.1:{port}/callback and http://localhost:{port}/callback (port 1024-65535) are automatically allowed without configuration. This follows RFC 8252 — loopback traffic never leaves the local machine.

After submission, you need to wait for the administrator's review. Only after the review is passed can it be used.

Application Web Origins ​

A Web Origin is separate from an OAuth Redirect URI. Redirect URIs control where authorization may return; Web Origins control which browser sites may call StarEcho APIs across origins. Application owners manage webOrigins while creating or editing an application, without asking a platform administrator to change server environment variables.

  • Production origins must use HTTPS and contain only scheme, host, and port. Paths, queries, fragments, user information, and wildcards are rejected.
  • http://localhost:{port} and http://127.0.0.1:{port} are development-only and must include an explicit port from 1024 through 65535.
  • An application may register up to 32 origins. An empty list denies browser API access from every external site.
  • /cf/api/public/{clientId}/... and /cf/http/public/{clientId}/... bind the URL clientId to that application's origins. Authorized browser APIs still validate the Access Token, Client ID, and permissions. CORS is not authentication.
  • STARECHO_CORS_ORIGIN is reserved for the platform's own fixed or operational origins; developer websites must not be added to it one by one.

Both POST /oauth/apps and PUT /oauth/apps/{clientId} accept:

json
{
  "webOrigins": [
    "https://app.example.com",
    "http://localhost:5100"
  ]
}

Before deploying this capability, run database_migrations/20260731-add-app-web-origins.sql, then deploy the new platform package. The migration does not trust any existing website automatically; the application owner must explicitly save its Web Origins.

2. Initiate Authorization ​

Redirect the user to the authorization page:

GET https://starecho.net/oauth/authorize

Query Parameters:

ParameterRequiredDescription
response_type✅Must be code
client_id✅Your application's Client ID
redirect_uri✅Redirect URI, must match the registered one
scope✅Space-separated scopes. Example: openid profile email
stateRecommendedRandom string to prevent CSRF attacks
nonceOptionalRandom string included in id_token to prevent replay attacks
code_challengeOptionalPKCE challenge code (required when using PKCE)
code_challenge_methodOptionalPKCE method: S256 or plain

Example:

https://starecho.net/oauth/authorize?
  response_type=code&
  client_id=YOUR_CLIENT_ID&
  redirect_uri=https://your-app.com/callback&
  scope=openid profile email&
  state=random_state_string

The user will see an authorization page showing your app's information and requested permissions. After the user approves, the browser will redirect to your callback URL:

https://your-app.com/callback?code=AUTHORIZATION_CODE&state=random_state_string

3. Exchange Token ​

Use the authorization code to request tokens from the token endpoint. Both application/x-www-form-urlencoded (OAuth 2.0 standard) and application/json are supported.

POST https://api.starecho.net/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=https%3A%2F%2Fyour-app.com%2Fcallback&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET

You can also use JSON format (Content-Type: application/json):

json
{
  "grant_type": "authorization_code",
  "code": "AUTHORIZATION_CODE",
  "redirect_uri": "https://your-app.com/callback",
  "client_id": "YOUR_CLIENT_ID",
  "client_secret": "YOUR_CLIENT_SECRET"
}

TIP

Most OAuth 2.0 client libraries use application/x-www-form-urlencoded by default. If your framework handles the token exchange automatically, you usually don't need to specify the Content-Type manually.

HTTP Basic authentication (client_secret_basic) is also supported. When using Basic auth, client_id and client_secret are not needed in the request body:

POST https://api.starecho.net/oauth/token
Authorization: Basic base64(client_id:client_secret)
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code&code=AUTHORIZATION_CODE&redirect_uri=https%3A%2F%2Fyour-app.com%2Fcallback

Success Response:

json
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "id_token": "eyJhbGciOiJSUzI1NiIs...",
  "refresh_token": "dGhpcyBpcyBhIHJlZnJlc2ggdG9rZW4...",
  "scope": "openid profile email"
}
FieldDescription
access_tokenAccess token (RS256 JWT, valid for 1 hour)
token_typeAlways Bearer
expires_inToken lifetime in seconds
id_tokenIdentity token (only when scope includes openid)
refresh_tokenRefresh token (valid for 30 days, rotated on each use)
scopeActually granted scopes

4. Get User Info ​

Use the access_token to request user information:

GET https://api.starecho.net/oauth/userinfo
Authorization: Bearer ACCESS_TOKEN

Success Response:

json
{
  "sub": "100001",
  "name": "StarUser",
  "email": "user@example.com",
  "gender": "male"
}
FieldScopeDescription
subAlwaysUser unique identifier (uid)
nameprofileDisplay name
genderprofileGender (male / female)
emailemailEmail address

5. Refresh Token ​

When the access_token expires, you can use the refresh_token to obtain a new set of tokens without requiring the user to re-authorize. This follows the OIDC Core 1.0 §12 specification.

POST https://api.starecho.net/oauth/token
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token&refresh_token=YOUR_REFRESH_TOKEN&client_id=YOUR_CLIENT_ID&client_secret=YOUR_CLIENT_SECRET

You can also use JSON format:

json
{
  "grant_type": "refresh_token",
  "refresh_token": "YOUR_REFRESH_TOKEN",
  "client_id": "YOUR_CLIENT_ID",
  "client_secret": "YOUR_CLIENT_SECRET"
}

PKCE Applications

If your application uses PKCE authentication (no client_secret), you only need to pass client_id — no client_secret is required.

Request Parameters:

ParameterRequiredDescription
grant_type✅Must be refresh_token
refresh_token✅The previously issued refresh token
client_id✅Your application's Client ID
client_secretConditionalRequired for non-PKCE applications
scopeOptionalCan request a subset of the original scopes (scope narrowing)

Success Response:

json
{
  "access_token": "eyJhbGciOiJSUzI1NiIs...",
  "token_type": "Bearer",
  "expires_in": 3600,
  "id_token": "eyJhbGciOiJSUzI1NiIs...",
  "refresh_token": "NEW_REFRESH_TOKEN",
  "scope": "openid profile email"
}

Token Rotation

Each time a refresh_token is used, the server returns a new refresh_token and the previous one is immediately invalidated. Always save and use the latest refresh_token.

If an invalidated refresh_token is reused (suspected token leakage), the server will automatically revoke all refresh tokens for that user in the application to protect account security.

Refresh Token Properties:

PropertyValue
Lifetime30 days
RotationNew token issued on each use, old token invalidated
Replay DetectionReuse of old token revokes all tokens for the application
Scope NarrowingCan request a subset of original scopes when refreshing

PKCE Support ​

For clients that cannot securely store a client_secret (native apps, SPAs), PKCE (Proof Key for Code Exchange) can be used instead of client secret authentication.

Flow ​

  1. Generate code_verifier: A random string of 43-128 characters
  2. Compute code_challenge: BASE64URL(SHA256(code_verifier))
  3. Initiate authorization with code_challenge and code_challenge_method=S256
  4. Exchange token with code_verifier (no client_secret needed)

Token Request Example (PKCE):

json
{
  "grant_type": "authorization_code",
  "code": "AUTHORIZATION_CODE",
  "redirect_uri": "https://your-app.com/callback",
  "client_id": "YOUR_CLIENT_ID",
  "code_verifier": "YOUR_CODE_VERIFIER"
}

Native App Integration Examples ​

Desktop (Loopback + PKCE):

// 1. Start a local HTTP server on a random port
// 2. Build the authorization URL
https://starecho.net/oauth/authorize?
  response_type=code&
  client_id=YOUR_CLIENT_ID&
  redirect_uri=http://127.0.0.1:12345/callback&
  scope=openid profile email&
  state=random_state&
  code_challenge=CHALLENGE&
  code_challenge_method=S256

// 3. Open the system browser for user authorization
// 4. Browser redirects to http://127.0.0.1:12345/callback?code=xxx
// 5. Local server receives the code and exchanges it for tokens via PKCE

Mobile (Custom URL Scheme + PKCE):

// Prerequisite: Register scheme "starecho" in app settings
https://starecho.net/oauth/authorize?
  response_type=code&
  client_id=YOUR_CLIENT_ID&
  redirect_uri=starecho://callback&
  scope=openid profile email&
  state=random_state&
  code_challenge=CHALLENGE&
  code_challenge_method=S256

// After authorization, the system opens the app via Deep Link:
// starecho://callback?code=AUTHORIZATION_CODE&state=random_state

Scopes ​

ScopeIncluded Information
openidReturns id_token with sub (user ID)
profileDisplay name (name), gender (gender)
emailEmail address (email)

ID Token ​

When the requested scope includes openid, the token response will include an id_token. This is an RS256-signed JWT containing the following claims:

ClaimDescription
subUser unique identifier (uid)
audYour Client ID
issIssuer URL
iatIssued at (Unix timestamp)
expExpiration time (Unix timestamp)
nonceThe nonce from the request (if provided)
nameDisplay name (when scope includes profile)
emailEmail address (when scope includes email)
genderGender (when scope includes profile)

You can verify the id_token signature using the public key from the JWKS endpoint.

Endpoint Reference ​

EndpointMethodURLDescription
DiscoveryGET/.well-known/openid-configurationOIDC service discovery
JWKSGET/oauth/jwksJSON Web Key Set public keys
AuthorizationGET/oauth/authorizeAuthorization page (frontend)
Authorization CheckGET/oauth/authorize/checkValidate authorization parameters
TokenPOST/oauth/tokenExchange authorization code for tokens / refresh tokens
UserInfoGET/oauth/userinfoGet user information

Base URLs

  • Frontend pages: https://starecho.net
  • API endpoints: https://api.starecho.net

Error Handling ​

Authorization Errors ​

When authorization fails, the user will be redirected to the callback URL with error parameters:

https://your-app.com/callback?error=access_denied&state=random_state_string
Error CodeDescription
access_deniedUser denied authorization
invalid_clientApplication not found
client_not_approvedApplication not yet approved
invalid_redirect_uriRedirect URI mismatch

Token Errors ​

The token endpoint returns errors in JSON format:

json
{
  "error": "invalid_grant",
  "error_description": "Authorization code expired"
}
Error CodeDescription
unsupported_grant_typeUnsupported grant type
invalid_grantAuthorization code is invalid, expired, or already used; or refresh token is invalid, expired, or revoked
invalid_clientClient ID or secret is incorrect
invalid_scopeRequested scope exceeds original authorization (refresh token only)

Security Best Practices ​

  1. Always use HTTPS — Web application redirect URIs must use the https:// protocol
  2. Validate state — Compare the state parameter in authorization request and callback to prevent CSRF
  3. Protect client_secret — Never expose secrets in frontend code, version control, or logs
  4. Use PKCE — Native apps and SPAs that cannot securely store secrets should use PKCE. All native apps are strongly recommended to enable PKCE
  5. Verify id_token — Validate signature, issuer, audience, and expiration
  6. Principle of least privilege — Only request the scopes you need

Native App Redirect URI Reference ​

PlatformRedirect URI FormatNotes
Web Apphttps://your-app.com/callbackMust be registered in app settings
Desktophttp://127.0.0.1:{port}/callbackAuto-allowed, port 1024-65535
Desktophttp://localhost:{port}/callbackAuto-allowed, port 1024-65535
Mobile{scheme}://callbackscheme must be registered in app settings

Code Examples ​

JavaScript / Node.js ​

javascript
// 1. Build authorization URL and redirect
const params = new URLSearchParams({
  response_type: 'code',
  client_id: 'YOUR_CLIENT_ID',
  redirect_uri: 'https://your-app.com/callback',
  scope: 'openid profile email',
  state: crypto.randomUUID(),
})
window.location.href = `https://starecho.net/oauth/authorize?${params}`

// 2. Callback: exchange authorization code for tokens
const tokenRes = await fetch('https://api.starecho.net/oauth/token', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({
    grant_type: 'authorization_code',
    code: authorizationCode,
    redirect_uri: 'https://your-app.com/callback',
    client_id: 'YOUR_CLIENT_ID',
    client_secret: 'YOUR_CLIENT_SECRET',
  }),
})
const tokens = await tokenRes.json()

// 3. Get user info
const userRes = await fetch('https://api.starecho.net/oauth/userinfo', {
  headers: { Authorization: `Bearer ${tokens.access_token}` },
})
const user = await userRes.json()
console.log(user.name, user.email)

Python ​

python
import requests

# Exchange token
token_res = requests.post('https://api.starecho.net/oauth/token', json={
    'grant_type': 'authorization_code',
    'code': authorization_code,
    'redirect_uri': 'https://your-app.com/callback',
    'client_id': 'YOUR_CLIENT_ID',
    'client_secret': 'YOUR_CLIENT_SECRET',
})
tokens = token_res.json()

# Get user info
user_res = requests.get('https://api.starecho.net/oauth/userinfo', headers={
    'Authorization': f'Bearer {tokens["access_token"]}'
})
user = user_res.json()
print(user['name'], user['email'])

© 2026 StarEcho