Exchange a code, or refresh a token
const url = 'https://api.nsin.ir/oauth/token';const options = { method: 'POST', headers: {'Content-Type': 'application/x-www-form-urlencoded'}, body: new URLSearchParams({ grant_type: 'authorization_code', client_id: 'example', client_secret: 'example', code: 'example', redirect_uri: 'example', code_verifier: 'example', refresh_token: 'example', scope: 'example' })};
try { const response = await fetch(url, options); const data = await response.json(); console.log(data);} catch (error) { console.error(error);}curl --request POST \ --url https://api.nsin.ir/oauth/token \ --header 'Content-Type: application/x-www-form-urlencoded' \ --data grant_type=authorization_code \ --data client_id=example \ --data client_secret=example \ --data code=example \ --data redirect_uri=example \ --data code_verifier=example \ --data refresh_token=example \ --data scope=exampleAccepts application/x-www-form-urlencoded (the OAuth default) and
JSON. A confidential client authenticates with client_secret_post or
HTTP Basic; a public client sends no secret and is protected by PKCE
alone.
grant_type=authorization_code — send code, redirect_uri,
client_id and code_verifier. The code is single-use and lives five
minutes. A code that never existed, has expired or has already been
redeemed all answer the same invalid_grant.
grant_type=refresh_token — send refresh_token and client_id.
Refresh tokens rotate: the response carries a new one and the presented
token is burnt. Presenting a burnt token is treated as a leak and
revokes every token of that login. scope may be sent to narrow the new
token; it can never widen.
A refresh_token is only issued when the offline_access scope was
granted. Rate limited to 60 requests per minute per IP and 600 per
minute per client_id.
Request Body required
Section titled “Request Body required ”The union of both grant types. Send it form-encoded or as JSON.
object
Confidential clients only, and only when not using HTTP Basic. A public client never sends one.
authorization_code only — the value from the redirect.
authorization_code only — must equal the one the code was issued for.
authorization_code only. The PKCE verifier, 43–128 characters from
[A-Za-z0-9._~-], whose SHA-256 produced the code_challenge.
refresh_token only.
refresh_token only, and optional: narrows the new token. It can
never widen — asking for more than was granted is invalid_scope.
The union of both grant types. Send it form-encoded or as JSON.
object
Confidential clients only, and only when not using HTTP Basic. A public client never sends one.
authorization_code only — the value from the redirect.
authorization_code only — must equal the one the code was issued for.
authorization_code only. The PKCE verifier, 43–128 characters from
[A-Za-z0-9._~-], whose SHA-256 produced the code_challenge.
refresh_token only.
refresh_token only, and optional: narrows the new token. It can
never widen — asking for more than was granted is invalid_scope.
Responses
Section titled “ Responses ”A fresh token set.
object
RS256 JWT. Verify it against the JWKS.
Access token lifetime in seconds.
Present only when offline_access was granted.
RS256 JWT carrying the identity claims the scopes allow.
The scopes actually granted
Example
{ "token_type": "Bearer"}invalid_request, unsupported_grant_type, invalid_grant
(unknown, expired, already-used or mismatched code or refresh
token — deliberately indistinguishable), invalid_scope, or
unauthorized_client for a client that has been switched off.
The RFC 6749 error shape, used by the NSIN SSO endpoints only. It is
deliberately different from Error: OAuth clients match on the machine
error code, and only show error_description to a developer.
object
The machine-readable code.
A human-readable explanation. Never match on this.
Example
{ "error": "invalid_grant", "error_description": "authorization code is invalid or expired"}invalid_client — the client id is unknown or the secret is wrong.
Carries WWW-Authenticate: Basic when Basic auth was attempted.
The RFC 6749 error shape, used by the NSIN SSO endpoints only. It is
deliberately different from Error: OAuth clients match on the machine
error code, and only show error_description to a developer.
object
The machine-readable code.
A human-readable explanation. Never match on this.
Example
{ "error": "invalid_grant", "error_description": "authorization code is invalid or expired"}Too many token requests.
The single error shape used by every endpoint.
object
Human-readable description of what went wrong.
Examples
{ "error": "slow_down"}