Home/API/OAuth

OAuth for every integration

OAuth guide

Quickstart

2

The canonical issuer is https://moddingflow.com. Desktop, mobile, CLI and Mod Manager integrations without protected credential storage use a public client_id with token_endpoint_auth_method=none. They never receive or send a static client_secret.

The app registers once automatically on first sign-in. For every authorization, generate a fresh verifier, S256 challenge, state, and nonce, open the system browser, validate state and iss, then exchange the code with client_id and the original verifier. The user clicks sign in and never copies a client_id or token.

Server and local integrations that can protect credentials may create an account-managed confidential application in Settings > Developer > OAuth applications. Moddingflow reveals its masked mfoc_ client_secret once and stores only a SHA-256 verifier plus a safe display prefix. Use client_secret_basic with Device Flow and refresh tokens. Rotating a secret immediately invalidates the previous credential, refresh sessions, and pending grants; already-issued access tokens expire within 15 minutes. Deleting the application revokes its grants.

method=nonehttps://moddingflow.com

POST https://moddingflow.com/oauth/register with Content-Type: application/json and the minimal body {"client_name":"My Mod Manager","redirect_uris":["http://127.0.0.1/callback"]}. No developer account, contact metadata, manual approval, or client secret is required; base scopes are assigned automatically.

The response contains client_id, registration_client_uri, and a one-time registration_access_token, and never client_secret. The app persists the public client_id automatically. Discard the management token if registration will not be managed, or keep it only in a protected product backend; never show it to the user.

/jsonhttp://127.0.0.1/callback"]}https://moddingflow.com/oauth/register

Authorization Code + PKCE

2

Generate 32 random bytes with the platform CSPRNG, encode the verifier as base64url without padding, and set code_challenge=BASE64URL(SHA256(verifier)) with code_challenge_method=S256. Also generate independent state and OpenID Connect nonce values.

Open GET https://moddingflow.com/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=CALLBACK&scope=openid%20mods:read&code_challenge=CHALLENGE&code_challenge_method=S256&state=STATE&nonce=NONCE in the system browser. Embedded webviews, implicit flow, password grant and prompt=none are unsupported.

method=S256prompt=nonechallenge=BASE64URLhttps://moddingflow.com/oauth/authorize?response_type=code&client_id=CLIENT_ID&redirect_uri=CALLBACK&scope=openid%20mods:read&code_challenge=CHALLENGE&code_challenge_method=S256&state=STATE&nonce=NONCE

Require exact state and iss=https://moddingflow.com in both success and error callbacks. Exchange the five-minute code at POST /oauth/token with grant_type=authorization_code, client_id, code, redirect_uri and code_verifier. No client secret is sent.

Validate an OpenID Connect ID token with discovery, JWKS, RS256 signature, issuer, audience, expiry and the original nonce. Access tokens live for 15 minutes.

POST /oauth/tokeniss=https//moddingflow.comtype=authorization_code

Device Flow

1

POST /oauth/device_authorization with client_id and scope, show verification_uri_complete plus user_code, and keep the full trust and permission disclosure visible. Poll POST /oauth/token with the Device Grant type at no faster than the returned interval.

Handle authorization_pending, slow_down, access_denied and expired_token exactly. Stop polling after denial, expiry or success and never log device_code, access tokens or refresh tokens.

POST /oauth/tokenPOST /oauth/device_authorization

Registration and callbacks

2

registration_access_token uses the mfrg_ prefix and is an administrative registration credential, not an OAuth access or refresh token. Discard it immediately when the registration lifecycle will not be managed. Otherwise keep it only in a protected product backend or secret manager and pass it as the separate managementToken argument. Never display it or ask the user to copy it.

GET registration_client_uri reads metadata without exposing or rotating the token. PUT replaces metadata and atomically rotates it. DELETE disables the client and revokes grants. Claiming the client permanently invalidates the management token.

New clients may use exact HTTPS callbacks, loopback IP callbacks on 127.0.0.1 or [::1] with a dynamic port, or reverse-domain custom schemes such as com.example.manager:/oauth/callback. localhost, fragments, userinfo, wildcard hosts and arbitrary schemes are rejected.

/oauth/callback

Scopes, consent and review

2

Anonymous registration may request any subset of openid, mods:read, files:download, install_plans:resolve and profile:read. Omitting scope selects that base set. The consent screen is shown on every authorization, even for previously granted permissions.

mods:readprofile:readfiles:downloadinstall_plans:resolve

To request mods:write, version or file management, webhooks, private data or team administration, claim the dynamic client in Developer Settings using client_id and the management token. Claim requires a same-origin account session and AAL2/2FA.

Sensitive requests move through pending, approved, or rejected; the administrator must record a reason and the decision is audited. rejected is fail-closed. agent:* and admin:* are internal-only and always rejected for third-party clients.

/2FAagent:*admin:*mods:write

Tokens, revocation and errors

2

Refresh tokens rotate on every use. Reuse of an old token revokes the entire family. POST /oauth/revoke is idempotent. Users can also revoke an app in Account Security; this closes consent, refresh families, pending codes and device grants, and makes current Bearer tokens fail immediately.

POST /oauth/revoke

Treat invalid_request, invalid_client, invalid_grant, invalid_scope, unauthorized_client, access_denied and interaction_required as terminal for the current attempt. Retry only documented transient failures and respect Retry-After with jitter.

Open registration is limited to a 16 KiB body, 10 requests per IP per hour and 50 per IP per day. Registration management, claim, device polling and authentication failures use separate shared fail-closed buckets.

SDK helpers

1

TypeScript: const request = await createOAuthAuthorizationRequest({ clientId, redirectUri, scopes: ["openid", "mods:read"] }); persist request.verifier, request.state and request.nonce, then open request.authorizationUrl.

Python: request = create_oauth_authorization_request(client_id=client_id, redirect_uri=redirect_uri, scopes=["openid", "mods:read"]); persist verifier, state and nonce, then open authorization_url.

C#: var request = ModdingflowPublicApiClient.CreateOAuthAuthorizationRequest(clientId, redirectUri, new[] { "openid", "mods:read" }); persist Verifier, State and Nonce, then open AuthorizationUrl.

mods:readid=client_iduri=redirect_uri
Moddingflow

Community, mods, and discussions for favorite games.

Project

NewsRatingAPIOAuthRules

Products

Moddingflow Premium

Site documents

Terms of UsePrivacy PolicyCookie PolicyLegal NoticeWithdrawal Information

Support

FAQ

© 2026 Moddingflow. All rights reserved.

Moddingflow
BETA

Games

All games (13)Recently added

My games

Oblivion RemasteredMinecraftStarfieldFallout 3The Witcher 3Oblivion
ForumNewsRating