nsindocs Search the docs⌘K
  1. Docs
  2. API reference
  3. SSO

Approve an authorization request

posthttps://api.nsin.ir/oauth/authorize

Auth: panel session — see API keys.

The server side of the panel's /oauth/authorize page. It is called by the NSIN panel with the signed-in user's session, never by a relying party: your application's part of this step is the browser redirect to {panel}/oauth/authorize?…, and the redirect back that follows.

Documented here because it defines the parameters your redirect must carry. state is required. PKCE is mandatory for every client: code_challenge_method must be S256 and code_challenge must be BASE64URL(SHA256(code_verifier)) with a verifier of 43–128 unreserved characters.

A first-party client asking only for identity scopes is approved silently. Everything else needs a stored grant — a third-party client for any scope, and ANY client, first-party included, that asks for nsin:full — because the grant carries the user's domain selection. The first request answers consent_required with the client's name, the human descriptions of the scopes and, when nsin:full is requested, a domain_access block (the current selection and every domain the user could pick). The panel shows the consent card with a domain picker and repeats the request with consent: true and domains ("all", or a list of domain ids the user has a role on). Every credential obtained through the grant — the access token, and any nsin_ key the app mints with it — is bound to the client and limited, live, to that selection; GET /whoami reports it as domain_access, and revoking the app in Connected apps leaves those credentials no domain at all.

prompt=none is the silent sign-in probe: a covering grant answers with the redirect as usual, anything else answers consent_required and nothing is written (the panel turns it into error=consent_required on the redirect_uri). prompt=consent always shows the card, prefilled from the existing grant, so a connected app can offer "change domain access".

Request body

Content type: application/json · required

  • client_idstringrequired
  • redirect_uristringrequired

    Must match one of the client's registered URIs exactly.

  • response_typestringrequired
    One of: "code"
  • scopestringrequired

    Space-separated. Must include openid.

  • statestringrequired

    Opaque value echoed back on the redirect. Required.

  • code_challengestringrequired
  • code_challenge_methodstringrequired
    One of: "S256"
  • noncestring

    Echoed in the ID token.

  • consentboolean

    Set by the panel when the user approves the consent card.

  • promptstring

    none never interacts and never writes a grant; consent always shows the card. Anything else is ignored.

    One of: "none" "consent"
  • domainsstring | array of integer

    The domain selection. Required with consent: true when nsin:full is requested, ignored otherwise: "all", or a non-empty list of domain ids the user owns or is a member of. An id outside that set answers 400 invalid_request with error_description "domain not accessible".

    Fields
    • Option 1string
    • Option 2array of integer

Responses

200Either the URL to send the browser to, or a request for consent.

Content type: application/json

  • Option 1object
    Fields
    • redirect_tostring

      The registered redirect_uri with code, state and iss appended.

  • Option 2object
    Fields
    • consent_requiredboolean
      One of: true
    • clientobject
      Fields
      • namestring
      • descriptionstring
      • first_partyboolean
    • scopesarray of object
      Item fields
      • idstring
      • descriptionstring
    • domain_accessobject

      Present only when nsin:full is requested.

      Fields
      • currentstring | array of integer

        The existing grant's selection, to prefill the picker — "all" when there is none.

        Fields
        • Option 1string
        • Option 2array of integer
      • domainsarray of object

        Every domain the user has a role on, sorted by name, at most 1000.

        Item fields
        • idinteger
        • namestring
        • rolestring
          One of: "owner" "admin" "editor" "viewer"
json
{
  "redirect_to": "https://app.example/auth/callback?code=nac_…&state=xyz&iss=https%3A%2F%2Fapi.nsin.ir"
}
400invalid_client, invalid_redirect_uri, unsupported_response_type, invalid_request (missing or malformed PKCE / state, or a missing or inaccessible domains selection) or invalid_scope.

Content type: application/json

  • errorstringrequired

    The machine-readable code.

  • error_descriptionstring

    A human-readable explanation. Never match on this.

json
{
  "error": "invalid_grant",
  "error_description": "authorization code is invalid or expired"
}
401No panel session, or a legacy session token with no session id.

Content type: application/json

  • errorstringrequired

    Human-readable description of what went wrong.

json
{
  "error": "read-only API key"
}
403The account is not active.

Content type: application/json

  • errorstringrequired

    Human-readable description of what went wrong.

json
{
  "error": "read-only API key"
}

Example request

bash
curl -X POST "https://api.nsin.ir/oauth/authorize" \
  -H "Authorization: Bearer $NSIN_API_KEY" \
  -H "Content-Type: application/json" \
  -d '{
  "client_id": "string",
  "redirect_uri": "string",
  "response_type": "code",
  "scope": "openid profile email offline_access",
  "state": "string",
  "code_challenge": "string",
  "code_challenge_method": "S256",
  "nonce": "string",
  "consent": true,
  "prompt": "none",
  "domains": "all"
}'
esc