{"openapi":"3.0.3","info":{"title":"WellGOV SSO API","version":"1.0.0","description":"API do WellGOV SSO \u2014 login \u00fanico (OAuth2\/OpenID Connect) e gest\u00e3o de usu\u00e1rios para sistemas integrados do governo.\n\nExistem dois jeitos de autenticar contra esta API:\n\n1. **OAuth2 \/ OpenID Connect (recomendado)** \u2014 para sistemas que fazem login de cidad\u00e3os em nome deles (\"Entrar com WellGOV\"). Fluxo Authorization Code: redirecione o usu\u00e1rio para `\/oauth\/authorize`, receba o `code` no seu `redirect_uri`, troque por um `access_token` em `POST \/oauth\/token`.\n2. **Login direto por e-mail\/senha** (`POST \/api\/v1\/login`) \u2014 para integra\u00e7\u00f5es internas\/administrativas que n\u00e3o passam pela tela de consentimento OAuth. Sujeito a limite de 5 tentativas\/minuto.\n\nCredenciais de sistema (client_id\/client_secret) s\u00e3o emitidas por um administrador do WellGOV ao cadastrar seu sistema \u2014 n\u00e3o h\u00e1 autoatendimento de cadastro.","contact":{"name":"Suporte WellGOV","url":"https:\/\/wellgov.net\/central-de-ajuda"}},"servers":[{"url":"https:\/\/www.wellgov.net","description":"Este servidor"}],"tags":[{"name":"OAuth2 \/ OIDC","description":"Fluxo de login \u00fanico para sistemas integrados"},{"name":"Autentica\u00e7\u00e3o direta","description":"Login por e-mail\/senha, sem passar pela tela de consentimento OAuth"},{"name":"Usu\u00e1rio autenticado","description":"Dados e sess\u00e3o do usu\u00e1rio dono do token"},{"name":"Usu\u00e1rios","description":"Gest\u00e3o de usu\u00e1rios (requer permiss\u00e3o manage-users)"},{"name":"Sistemas","description":"Sistemas integrados ao WellGOV"},{"name":"Administra\u00e7\u00e3o OAuth","description":"Cadastro e gest\u00e3o de clientes OAuth (requer permiss\u00e3o manage-oauth-clients)"},{"name":"Logout \u00fanico","description":"Single Logout entre sistemas integrados"},{"name":"Status","description":"Verifica\u00e7\u00e3o de disponibilidade da API"}],"components":{"securitySchemes":{"oauth2":{"type":"oauth2","description":"Authorization Code Grant \u2014 fluxo padr\u00e3o para autenticar um cidad\u00e3o em nome do seu sistema.","flows":{"authorizationCode":{"authorizationUrl":"\/oauth\/authorize","tokenUrl":"\/oauth\/token","scopes":{"openid":"Identifica\u00e7\u00e3o b\u00e1sica (sub \/ user_id)","profile":"Dados do perfil: nome, avatar, data de nascimento","email":"Endere\u00e7o de e-mail e status de verifica\u00e7\u00e3o","cpf":"CPF do cidad\u00e3o","phone":"N\u00famero de telefone","address":"Endere\u00e7o completo","roles":"Papel do usu\u00e1rio neste sistema (ex.: administrador)"}}}},"bearerAuth":{"type":"http","scheme":"bearer","bearerFormat":"Passport access token","description":"Token obtido via OAuth2 ou via POST \/api\/v1\/login. Enviar como `Authorization: Bearer {token}`."},"basicAuth":{"type":"http","scheme":"basic","description":"client_id:client_secret do seu sistema \u2014 usado s\u00f3 no endpoint de introspec\u00e7\u00e3o de token."}},"schemas":{"Error":{"type":"object","properties":{"message":{"type":"string"},"errors":{"type":"object","additionalProperties":{"type":"array","items":{"type":"string"}}}}},"User":{"type":"object","properties":{"id":{"type":"integer"},"name":{"type":"string"},"email":{"type":"string","format":"email"},"phone":{"type":"string","nullable":true},"is_active":{"type":"boolean"},"roles":{"type":"array","items":{"type":"object","properties":{"name":{"type":"string"}}}}}},"Sistema":{"type":"object","properties":{"nome":{"type":"string"},"url_base":{"type":"string","format":"uri"},"icone":{"type":"string"},"orgao_nome":{"type":"string","nullable":true}}},"LoginSuccess":{"type":"object","properties":{"message":{"type":"string"},"user":{"$ref":"#\/components\/schemas\/User"},"access_token":{"type":"string"},"token_type":{"type":"string","example":"Bearer"},"expires_at":{"type":"string","format":"date-time"}}},"TwoFactorRequired":{"type":"object","properties":{"message":{"type":"string"},"requires_two_factor":{"type":"boolean","example":true},"user_id":{"type":"integer"},"two_factor_token":{"type":"string","description":"Enviar de volta em POST \/api\/v1\/login\/2fa junto com o c\u00f3digo do autenticador."}}}}},"security":[{"bearerAuth":[]}],"paths":{"\/api\/health":{"get":{"tags":["Status"],"summary":"Verifica se a API est\u00e1 no ar","security":[],"responses":{"200":{"description":"API dispon\u00edvel","content":{"application\/json":{"schema":{"type":"object","properties":{"status":{"type":"string","example":"ok"},"timestamp":{"type":"string","format":"date-time"},"version":{"type":"string"}}}}}}}}},"\/oauth\/authorize":{"get":{"tags":["OAuth2 \/ OIDC"],"summary":"Inicia o login (tela de consentimento)","description":"Redirecione o navegador do cidad\u00e3o para esta URL. Se ele n\u00e3o estiver logado no WellGOV, ver\u00e1 a tela de login primeiro; em seguida, a tela de consentimento pedindo aprova\u00e7\u00e3o dos escopos solicitados.","security":[],"parameters":[{"name":"client_id","in":"query","required":true,"schema":{"type":"string"}},{"name":"redirect_uri","in":"query","required":true,"schema":{"type":"string","format":"uri"},"description":"Precisa ser exatamente igual a uma das redirect_uris cadastradas para o seu client_id."},{"name":"response_type","in":"query","required":true,"schema":{"type":"string","enum":["code"]}},{"name":"scope","in":"query","required":false,"schema":{"type":"string"},"description":"Escopos separados por espa\u00e7o, ex.: \"openid profile email\". Padr\u00e3o: openid profile email."},{"name":"state","in":"query","required":false,"schema":{"type":"string"},"description":"Valor opaco devolvido sem altera\u00e7\u00e3o no redirect \u2014 recomendado para prevenir CSRF."}],"responses":{"302":{"description":"Redireciona para o login (se necess\u00e1rio) ou para a tela de consentimento"}}}},"\/oauth\/token":{"post":{"tags":["OAuth2 \/ OIDC"],"summary":"Troca o c\u00f3digo de autoriza\u00e7\u00e3o (ou refresh token) por um access token","security":[],"requestBody":{"required":true,"content":{"application\/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"grant_type":{"type":"string","enum":["authorization_code","refresh_token"]},"client_id":{"type":"string"},"client_secret":{"type":"string"},"redirect_uri":{"type":"string","format":"uri"},"code":{"type":"string","description":"Obrigat\u00f3rio quando grant_type=authorization_code"},"refresh_token":{"type":"string","description":"Obrigat\u00f3rio quando grant_type=refresh_token"}},"required":["grant_type","client_id","client_secret"]}}}},"responses":{"200":{"description":"Token emitido","content":{"application\/json":{"schema":{"type":"object","properties":{"token_type":{"type":"string","example":"Bearer"},"expires_in":{"type":"integer"},"access_token":{"type":"string"},"refresh_token":{"type":"string"}}}}}},"400":{"description":"C\u00f3digo, client ou grant inv\u00e1lido","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/api\/v1\/oauth\/introspect":{"post":{"tags":["OAuth2 \/ OIDC"],"summary":"Verifica se um access token ainda \u00e9 v\u00e1lido (RFC 7662)","description":"Alternativa a chamar GET \/api\/v1\/user s\u00f3 para checar validade. S\u00f3 inspeciona tokens emitidos para o pr\u00f3prio client autenticado.","security":[{"basicAuth":[]}],"requestBody":{"required":true,"content":{"application\/x-www-form-urlencoded":{"schema":{"type":"object","properties":{"token":{"type":"string"}},"required":["token"]}}}},"responses":{"200":{"description":"Resultado da introspec\u00e7\u00e3o (active=false se o token for inv\u00e1lido\/expirado\/de outro client)","content":{"application\/json":{"schema":{"type":"object","properties":{"active":{"type":"boolean"},"scope":{"type":"string"},"client_id":{"type":"string"},"username":{"type":"string"},"sub":{"type":"string"},"exp":{"type":"integer","nullable":true},"iat":{"type":"integer"},"token_type":{"type":"string"},"user":{"$ref":"#\/components\/schemas\/User"}}}}}},"401":{"description":"client_id\/client_secret ausente ou inv\u00e1lido"}}}},"\/api\/v1\/login":{"post":{"tags":["Autentica\u00e7\u00e3o direta"],"summary":"Login por e-mail e senha","description":"Limite de 5 requisi\u00e7\u00f5es por minuto por IP. Se a conta tiver 2FA ativo, retorna 200 com requires_two_factor=true em vez do token \u2014 complete em POST \/api\/v1\/login\/2fa.","security":[],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","properties":{"email":{"type":"string","format":"email"},"password":{"type":"string","format":"password"},"device_name":{"type":"string"}},"required":["email","password","device_name"]}}}},"responses":{"200":{"description":"Login conclu\u00eddo, ou desafio de 2FA pendente","content":{"application\/json":{"schema":{"oneOf":[{"$ref":"#\/components\/schemas\/LoginSuccess"},{"$ref":"#\/components\/schemas\/TwoFactorRequired"}]}}}},"422":{"description":"Credenciais inv\u00e1lidas ou conta desativada","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}},"429":{"description":"Muitas tentativas \u2014 aguarde antes de tentar novamente"}}}},"\/api\/v1\/login\/2fa":{"post":{"tags":["Autentica\u00e7\u00e3o direta"],"summary":"Completa o login confirmando o c\u00f3digo de 2FA","security":[],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","properties":{"two_factor_token":{"type":"string","description":"Recebido em POST \/api\/v1\/login"},"two_factor_code":{"type":"string","description":"C\u00f3digo de 6 d\u00edgitos do autenticador, ou um c\u00f3digo de recupera\u00e7\u00e3o de 10 caracteres"},"device_name":{"type":"string"}},"required":["two_factor_token","two_factor_code","device_name"]}}}},"responses":{"200":{"description":"Login conclu\u00eddo","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/LoginSuccess"}}}},"422":{"description":"Token de desafio expirado ou c\u00f3digo inv\u00e1lido","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/api\/v1\/logout":{"post":{"tags":["Usu\u00e1rio autenticado"],"summary":"Revoga o token usado na requisi\u00e7\u00e3o","responses":{"200":{"description":"Logout conclu\u00eddo"}}}},"\/api\/v1\/refresh":{"post":{"tags":["Usu\u00e1rio autenticado"],"summary":"Revoga o token atual e emite um novo","description":"Para trocar tokens de login direto (n\u00e3o-OAuth). Fluxos OAuth devem usar POST \/oauth\/token com grant_type=refresh_token.","responses":{"200":{"description":"Novo token emitido","content":{"application\/json":{"schema":{"type":"object","properties":{"access_token":{"type":"string"},"token_type":{"type":"string"},"expires_at":{"type":"string","format":"date-time"}}}}}}}}},"\/api\/v1\/user":{"get":{"tags":["Usu\u00e1rio autenticado"],"summary":"Dados do usu\u00e1rio dono do token (endpoint UserInfo do OIDC)","description":"Os campos retornados dependem dos escopos concedidos ao token \u2014 cada claim s\u00f3 aparece se o escopo correspondente foi autorizado.","responses":{"200":{"description":"Claims do usu\u00e1rio","content":{"application\/json":{"schema":{"type":"object","properties":{"sub":{"type":"string"},"name":{"type":"string"},"email":{"type":"string"},"email_verified":{"type":"boolean"},"picture":{"type":"string","nullable":true},"birthdate":{"type":"string","nullable":true},"cpf":{"type":"string"},"phone_number":{"type":"string"},"roles":{"type":"array","items":{"type":"string"}},"address":{"type":"object"}}}}}},"401":{"description":"Token ausente, inv\u00e1lido ou expirado"}}}},"\/api\/v1\/sistemas":{"get":{"tags":["Sistemas"],"summary":"Lista sistemas integrados ativos","responses":{"200":{"description":"Lista de sistemas","content":{"application\/json":{"schema":{"type":"object","properties":{"sistemas":{"type":"object","properties":{"data":{"type":"array","items":{"$ref":"#\/components\/schemas\/Sistema"}}}}}}}}}}}},"\/api\/v1\/users":{"get":{"tags":["Usu\u00e1rios"],"summary":"Lista usu\u00e1rios (requer permiss\u00e3o manage-users)","parameters":[{"name":"search","in":"query","schema":{"type":"string"},"description":"Busca por nome ou e-mail"},{"name":"role","in":"query","schema":{"type":"string"}},{"name":"status","in":"query","schema":{"type":"string","enum":["active","inactive"]}},{"name":"per_page","in":"query","schema":{"type":"integer","default":15}}],"responses":{"200":{"description":"P\u00e1gina de usu\u00e1rios","content":{"application\/json":{"schema":{"type":"object","properties":{"users":{"type":"object"}}}}}},"403":{"description":"Sem permiss\u00e3o manage-users"}}},"post":{"tags":["Usu\u00e1rios"],"summary":"Cria um usu\u00e1rio (requer permiss\u00e3o manage-users)","requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"},"password":{"type":"string"},"phone":{"type":"string"},"is_active":{"type":"boolean"},"roles":{"type":"array","items":{"type":"string"}}},"required":["name","email","password"]}}}},"responses":{"201":{"description":"Usu\u00e1rio criado","content":{"application\/json":{"schema":{"type":"object","properties":{"message":{"type":"string"},"user":{"$ref":"#\/components\/schemas\/User"}}}}}},"422":{"description":"Dados inv\u00e1lidos","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/api\/v1\/users\/{user}":{"get":{"tags":["Usu\u00e1rios"],"summary":"Detalhes de um usu\u00e1rio","description":"Permitido para o pr\u00f3prio usu\u00e1rio (autoconsulta) ou para quem tem a permiss\u00e3o manage-users.","parameters":[{"name":"user","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Usu\u00e1rio","content":{"application\/json":{"schema":{"type":"object","properties":{"user":{"$ref":"#\/components\/schemas\/User"}}}}}},"403":{"description":"Acesso negado"}}},"put":{"tags":["Usu\u00e1rios"],"summary":"Atualiza um usu\u00e1rio","description":"Campos permitidos dependem de quem est\u00e1 chamando: o pr\u00f3prio usu\u00e1rio pode trocar nome\/e-mail\/telefone\/senha; s\u00f3 quem tem manage-users pode alterar pap\u00e9is.","parameters":[{"name":"user","in":"path","required":true,"schema":{"type":"integer"}}],"requestBody":{"content":{"application\/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"email":{"type":"string"},"phone":{"type":"string"},"password":{"type":"string"},"is_active":{"type":"boolean"},"roles":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"200":{"description":"Usu\u00e1rio atualizado"},"403":{"description":"Acesso negado"},"422":{"description":"Dados inv\u00e1lidos","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}},"delete":{"tags":["Usu\u00e1rios"],"summary":"Exclui um usu\u00e1rio (requer permiss\u00e3o manage-users)","parameters":[{"name":"user","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Usu\u00e1rio exclu\u00eddo"},"400":{"description":"N\u00e3o \u00e9 poss\u00edvel excluir a pr\u00f3pria conta"}}}},"\/api\/v1\/users\/{user}\/toggle-status":{"patch":{"tags":["Usu\u00e1rios"],"summary":"Ativa\/desativa um usu\u00e1rio (requer permiss\u00e3o manage-users)","parameters":[{"name":"user","in":"path","required":true,"schema":{"type":"integer"}}],"responses":{"200":{"description":"Status alterado"},"400":{"description":"N\u00e3o \u00e9 poss\u00edvel desativar a pr\u00f3pria conta"}}}},"\/api\/v1\/oauth\/register":{"post":{"tags":["Administra\u00e7\u00e3o OAuth"],"summary":"Registra um novo cliente OAuth (requer permiss\u00e3o manage-users)","requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"redirect_uris":{"type":"array","items":{"type":"string","format":"uri"}},"grant_types":{"type":"array","items":{"type":"string","enum":["authorization_code","client_credentials","password","refresh_token","urn:ietf:params:oauth:grant-type:device_code"]}}},"required":["name","redirect_uris","grant_types"]}}}},"responses":{"201":{"description":"Cliente criado \u2014 client_secret s\u00f3 \u00e9 exibido nesta resposta, guarde com seguran\u00e7a"},"409":{"description":"J\u00e1 existe um cliente com esse nome"},"422":{"description":"Dados inv\u00e1lidos","content":{"application\/json":{"schema":{"$ref":"#\/components\/schemas\/Error"}}}}}}},"\/api\/v1\/oauth\/clients":{"get":{"tags":["Administra\u00e7\u00e3o OAuth"],"summary":"Lista clientes OAuth (requer permiss\u00e3o manage-oauth-clients)","responses":{"200":{"description":"Lista de clientes"},"403":{"description":"Acesso negado"}}}},"\/api\/v1\/oauth\/clients\/{clientId}":{"put":{"tags":["Administra\u00e7\u00e3o OAuth"],"summary":"Atualiza um cliente OAuth (requer permiss\u00e3o manage-users)","parameters":[{"name":"clientId","in":"path","required":true,"schema":{"type":"string"}}],"requestBody":{"content":{"application\/json":{"schema":{"type":"object","properties":{"name":{"type":"string"},"redirect_uris":{"type":"array","items":{"type":"string"}},"grant_types":{"type":"array","items":{"type":"string"}}}}}}},"responses":{"200":{"description":"Cliente atualizado"},"404":{"description":"Cliente n\u00e3o encontrado"}}}},"\/api\/v1\/slo":{"post":{"tags":["Logout \u00fanico"],"summary":"Recebe uma notifica\u00e7\u00e3o de Single Logout de outro sistema integrado","description":"Assinado com um token espec\u00edfico de SLO \u2014 n\u00e3o \u00e9 o access token comum. Revoga todos os tokens do usu\u00e1rio indicado.","security":[],"requestBody":{"required":true,"content":{"application\/json":{"schema":{"type":"object","properties":{"slo_token":{"type":"string"},"user_id":{"type":"integer"},"timestamp":{"type":"integer"}},"required":["slo_token","user_id","timestamp"]}}}},"responses":{"200":{"description":"Logout \u00fanico processado"},"401":{"description":"Token de SLO inv\u00e1lido"}}}}}}