Developers ========== Sabiá is the identity provider of the SUS systems that partner with LAIS: users sign in to your system with their Sabiá account (CPF or email and password, or gov.br) without creating another account. The integration uses `OAuth 2.0 `__. Register your application ~~~~~~~~~~~~~~~~~~~~~~~~~ In the **desenvolvedores** (developers) portal, sign in with your Sabiá account, create the application and enter its name, description, logo, contact email, domains and redirect URIs (``http`` or ``https``). Also choose: - **Client type:** *confidential* (the ``client_secret`` lives on a server) or *public* (an app or web page that cannot keep a secret; it sends only the ``client_id`` and should use PKCE). - **Grant type:** each application uses **one** type (authorization code, implicit, password or client credentials), plus token refresh. Requesting another type returns ``unauthorized_client``. - The **optional scopes** the application uses (see below). The mandatory scopes are always on. The ``client_secret`` is shown **only once**, when the application is created or a new one is generated: copy it and store it in a vault. If you lose it, generate another one; the previous one stops working. **Members:** the owner adds other people by CPF or email, as *admin* (edits the application, scopes, secret and members) or *developer* (read only). Only the owner removes admins. Endpoints ~~~~~~~~~ All on the Sabiá login domain (in production, ``https://login.sabia.ufrn.br``): =================================== ======================================================================== Endpoint Use =================================== ======================================================================== ``GET /oauth/authorize/`` Sign-in and consent screen; returns the code (or the token, in the implicit grant) ``POST /oauth/token/`` Exchanges the code, password, client credentials or refresh token for tokens ``POST /oauth/revoke_token/`` Revokes an access or refresh token (`RFC 7009 `__) ``POST /api/perfil/dados/`` User data, according to the scopes ``GET /logout/`` Ends the user's Sabiá session =================================== ======================================================================== Recommended flow: authorization code with PKCE ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 1. Redirect the user to the authorization endpoint with a random ``state`` and the PKCE challenge (``S256``): .. code-block:: text https://login.sabia.ufrn.br/oauth/authorize/?response_type=code&client_id=CLIENT_ID &redirect_uri=https%3A%2F%2Fyoursystem.example%2Fcallback%2F&scope=cpf+email+dados_publicos &state=STATE&code_challenge=CHALLENGE&code_challenge_method=S256 2. The user signs in and, the first time, authorizes the application. Sabiá returns to the ``redirect_uri`` with ``?code=...&state=...`` (or ``?error=access_denied&state=...`` if the user cancels). Check the ``state``. 3. Exchange the code (valid for **10 minutes**, single use) for tokens: .. code-block:: bash curl -X POST https://login.sabia.ufrn.br/oauth/token/ \ -d grant_type=authorization_code -d code=CODE \ -d redirect_uri=https://yoursystem.example/callback/ \ -d client_id=CLIENT_ID -d client_secret=CLIENT_SECRET -d code_verifier=VERIFIER .. code-block:: json {"access_token": "...", "expires_in": 86400, "token_type": "Bearer", "scope": "cpf email dados_publicos", "refresh_token": "..."} 4. Request the user's data (``POST``, with the desired scopes in the body): .. code-block:: bash curl -X POST https://login.sabia.ufrn.br/api/perfil/dados/ \ -H "Authorization: Bearer ACCESS_TOKEN" -d "scope=cpf email dados_publicos" Client credentials can go in the body (``application/x-www-form-urlencoded`` or ``multipart/form-data``) or in HTTP Basic. The ``redirect_uri`` must be **exactly** one of the registered URIs; if omitted, the first one is used. Scopes and data ~~~~~~~~~~~~~~~ ``/api/perfil/dados/`` always returns ``uuid`` (the user's public, stable identifier), ``name`` and ``avatar`` (photo URL), plus the fields of the requested scopes that the token has and that are enabled for the application. Scopes that are not enabled are ignored without error. =================================== ======================== ===================================================== Scope Type Content =================================== ======================== ===================================================== ``cpf`` mandatory CPF (11 digits); ``null`` for foreigners ``email`` mandatory Email ``dados_publicos`` mandatory Name and photo ``cnes`` optional CNES employment records (cached lookup) ``receita_federal`` optional Federal Revenue data, or ``{"erro": ...}`` if not validated ``experiencia_profissional`` optional List of work experiences (facility, occupation, dates, municipality, description), or ``{"erro": ...}`` if empty ``formacao_academica`` optional List of education entries (institution, course, degree, dates, municipality, description), or ``{"erro": ...}`` if empty =================================== ======================== ===================================================== Use the ``uuid`` as the user key in your system: the CPF may be missing (foreigners) and the email may change. Token lifetimes ~~~~~~~~~~~~~~~ - Authorization code: 10 minutes, single use. - Access token: 24 hours. - Refresh token: 30 days, with rotation: each use returns a new pair and invalidates the previous one. Always keep the latest. The implicit grant does not issue refresh tokens. - Revoking the refresh token also revokes the access token. Revocation returns ``200`` even for unknown tokens. Errors ~~~~~~ ``/oauth/token/`` responds in the RFC 6749 format (``{"error": ..., "error_description": ...}``): ``invalid_client`` (401), ``invalid_request``, ``invalid_grant`` (expired code or token, invalid ``code_verifier``, inactive user), ``invalid_scope``, ``unauthorized_client`` and ``unsupported_grant_type``. When the service is unavailable, ``503`` and ``temporarily_unavailable``. On ``/api/perfil/dados/``: ``401`` without a valid token (including client-credentials tokens, which have no user), ``405`` with ``GET``, and ``506`` with ``{"erro": {"codigo": 9}}`` (no valid scope) or ``codigo 10`` (inactive application). When the refresh returns ``invalid_grant``, send the user through sign-in again. Good practices ~~~~~~~~~~~~~~ - Prefer the authorization code with PKCE ``S256`` and ``state``; avoid the implicit and password grants. - Keep the ``client_secret`` on the server only; apps and web pages must be public clients. - Request only the scopes you need: the user sees the list on the consent screen. - Never store Sabiá passwords or reproduce the sign-in screen inside your system. Partner API (API token) ~~~~~~~~~~~~~~~~~~~~~~~ To create and look up user accounts straight from your server, without the user on screen, use the partner API at ``https://api.sabia.ufrn.br/usuarios/``. It only serves applications whose API access was granted by the Sabiá team (ask support) and uses the application's **API token**, generated on the application details screen of the developers portal. The token is shown only once; generating another one invalidates the previous one. Send the token in the ``Authorization: Token `` header. Without a token or with an invalid one the response is ``401``; without API access, ``403``. - ``GET /usuarios/{cpf_or_email}/``: account data (profile, education, Receita Federal and CNES); ``404`` if missing. - ``GET /usuarios/?cpfs=,&page=1``: several accounts by CPF, 100 per page. - ``POST /usuarios/`` as a form with ``cpf``, ``email``, ``nome``, ``genero`` (``M`` or ``F``) and ``data_de_nascimento`` (``YYYY-MM-DD``): creates one account (``201``). - ``POST /usuarios/`` as JSON with ``alunos``: creates several accounts at once, each with ``formacoes`` (``instituicao`` and ``curso`` by e-MEC code, ``municipio`` by IBGE code, ``data_inicio``, optional ``data_fim``, ``descricao`` and ``grau_formacao``). If any item has an error, no account is created and the ``400`` response lists every error. - ``DELETE /usuarios/{cpf_or_email}/``: removes an account that never signed in to Sabiá (``401`` if it did). The new account gets an email with a link to create the password, valid for 30 days; after that, the person uses "Forgot your password?" on the sign-in screen. Upgrade to Sabiá 2: what changes for partners ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ - **Unchanged:** the endpoints above, the ``client_id``, the ``client_secret`` and the redirect URIs. - **Tokens and sessions are not migrated:** after the upgrade, old refresh tokens get ``invalid_grant`` and every user signs in again. Handle this error by requesting a new authorization. - ``/api/perfil/dados/`` requires ``POST`` with ``scope`` in the body and no longer returns ``senha_criptografada``; it now returns the user's ``uuid``. - Sabiá 1 API tokens whose key was the ``client_id`` are not migrated. Generate an API token in the developers portal and replace the key in the ``Authorization: Token`` header; partner API endpoints and formats stay the same. Applications that had API access in Sabiá 1 keep it. - Removed from the partner API: ``/usuarios/{cpf}/logout/``, ``/usuarios/stats/``, ``/stats/``, ``/cns/`` and ``/cns-retsus/``, which now answer ``410``. Help ~~~~ Integration questions: sabia@atendimento.lais.ufrn.br, stating the application name and ``client_id`` (never send the ``client_secret``).