> ## Documentation Index
> Fetch the complete documentation index at: https://docs-platform.services-valid.com.br/llms.txt
> Use this file to discover all available pages before exploring further.

# Autenticação OAuth 2.0

> Obtenha seu token de acesso para usar as APIs do Flexdoc

## Visão Geral

Todas as requisições para as APIs do Flexdoc exigem autenticação via **OAuth 2.0** usando o fluxo **Client Credentials**. Esta página permite que você gere seu token de acesso diretamente no playground.

<Steps>
  <Step title="1. Use suas credenciais">
    Informe o Client ID e Client Secret fornecidos pela equipe Flexdoc
  </Step>

  <Step title="2. Gere o token">
    Execute a requisição no playground ao lado
  </Step>

  <Step title="3. Copie o access_token">
    Armazene o token retornado para usar nas próximas requisições
  </Step>

  <Step title="4. Use nas APIs">
    Adicione no header: `Authorization: Bearer {access_token}`
  </Step>
</Steps>

***

## Credenciais de Desenvolvimento

Para testes no ambiente de desenvolvimento, use as credenciais abaixo:

<CodeGroup>
  ```bash Credenciais DEV theme={"theme":"catppuccin-latte"}
  CLIENT_ID=teste-api
  CLIENT_SECRET=pwR8GPipQvifGhSIH7cH07OOCXd2C8Hg
  ```
</CodeGroup>

<Warning>
  As credenciais acima são **exclusivas para ambiente de desenvolvimento (DES)**.

  Para produção, solicite suas credenciais através do email `integracao@flexdoc.com.br`
</Warning>

***

## Request Parameters

<Note>
  **Teste Agora!** Use o playground interativo **"Try It"** na lateral direita desta página →

  Preencha os campos abaixo e clique em "Send" para gerar seu token instantaneamente.
</Note>

### Body Parameters (application/x-www-form-urlencoded)

<ParamField body="grant_type" default="client_credentials" type="string" required>
  Tipo de concessão OAuth 2.0. Deve ser sempre `client_credentials`
</ParamField>

<ParamField body="client_id" default="teste-api" type="string" required>
  Identificador único do cliente fornecido pela Flexdoc

  **Exemplo DEV:** `teste-api`
</ParamField>

<ParamField body="client_secret" default="pwR8GPipQvifGhSIH7cH07OOCXd2C8Hg" type="string" required>
  Chave secreta do cliente fornecida pela Flexdoc

  **Exemplo DEV:** `pwR8GPipQvifGhSIH7cH07OOCXd2C8Hg`

  <Warning>
    **Nunca** compartilhe ou exponha seu client\_secret em repositórios públicos ou código frontend
  </Warning>
</ParamField>

***

## Response

<ResponseField name="access_token" type="string" required>
  Token JWT para autenticação nas APIs. Use no header `Authorization: Bearer {token}`

  **Formato:** JWT (JSON Web Token)

  **Exemplo:** `eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9...`
</ResponseField>

<ResponseField name="token_type" type="string">
  Tipo do token. Sempre retorna `Bearer`
</ResponseField>

<ResponseField name="expires_in" type="integer">
  Tempo de validade do token em segundos

  **Padrão:** `3600` (1 hora)
</ResponseField>

<ResponseField name="refresh_expires_in" type="integer">
  Tempo de validade do refresh token em segundos (se aplicável)
</ResponseField>

<ResponseField name="scope" type="string">
  Escopos de acesso concedidos ao token

  **Exemplo:** `profile email`
</ResponseField>

<ResponseExample>
  ```json Sucesso (200 OK) theme={"theme":"catppuccin-latte"}
  {
    "access_token": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJxNzJMVEMyVXQ4VjhfYTBYMjRMU2g3MThsUndVdWFQRDVWNXVhX0pIaW5VIn0.eyJleHAiOjE3NDU0MjE2MzMsImlhdCI6MTc0NTQxODAzMywianRpIjoiZjc4YzA5ZjAtMTBkZS00NzQwLWI4ODEtOGY0YWQwZjBmZmZkIiwiaXNzIjoiaHR0cHM6Ly9hdXRoLWRlcy5mbGV4ZG9jLWFwaXMuY29tLmJyL3JlYWxtcy9mbGV4aW1hZ2UiLCJhdWQiOiJhY2NvdW50Iiwic3ViIjoiNGU3YzY4YWItNzQxZi00ZDhjLWJiNGUtMWY3MzI5ZDkwNzAzIiwidHlwIjoiQmVhcmVyIiwiYXpwIjoidGVzdGUtYXBpIn0...",
    "expires_in": 3600,
    "refresh_expires_in": 0,
    "token_type": "Bearer",
    "not-before-policy": 0,
    "scope": "profile email"
  }
  ```

  ```json Erro 401 - Credenciais Inválidas theme={"theme":"catppuccin-latte"}
  {
    "error": "invalid_client",
    "error_description": "Invalid client credentials"
  }
  ```

  ```json Erro 400 - Parâmetros Incorretos theme={"theme":"catppuccin-latte"}
  {
    "error": "invalid_request",
    "error_description": "Missing form parameter: grant_type"
  }
  ```
</ResponseExample>

***

## Exemplos de Código

<CodeGroup>
  ```bash cURL theme={"theme":"catppuccin-latte"}
  curl -X POST "https://auth-des.flexdoc-apis.com.br/realms/fleximage/protocol/openid-connect/token" \
    -H "Content-Type: application/x-www-form-urlencoded" \
    -d "grant_type=client_credentials" \
    -d "client_id=teste-api" \
    -d "client_secret=pwR8GPipQvifGhSIH7cH07OOCXd2C8Hg"
  ```

  ```javascript JavaScript (Axios) theme={"theme":"catppuccin-latte"}
  const axios = require('axios');
  const qs = require('qs');

  async function getAccessToken() {
    try {
      const response = await axios.post(
        'https://auth-des.flexdoc-apis.com.br/realms/fleximage/protocol/openid-connect/token',
        qs.stringify({
          grant_type: 'client_credentials',
          client_id: 'teste-api',
          client_secret: 'pwR8GPipQvifGhSIH7cH07OOCXd2C8Hg'
        }),
        {
          headers: {
            'Content-Type': 'application/x-www-form-urlencoded'
          }
        }
      );

      const { access_token, expires_in } = response.data;
      console.log('Token obtido com sucesso!');
      console.log('Expira em:', expires_in, 'segundos');

      return access_token;
    } catch (error) {
      console.error('Erro ao obter token:', error.response?.data || error.message);
      throw error;
    }
  }

  // Uso
  getAccessToken().then(token => {
    console.log('Token:', token);
  });
  ```

  ```python Python (Requests) theme={"theme":"catppuccin-latte"}
  import requests
  from datetime import datetime, timedelta

  def get_access_token():
      """
      Obtém token de acesso OAuth 2.0
      Retorna: (access_token, expiration_time)
      """
      url = "https://auth-des.flexdoc-apis.com.br/realms/fleximage/protocol/openid-connect/token"

      data = {
          'grant_type': 'client_credentials',
          'client_id': 'teste-api',
          'client_secret': 'pwR8GPipQvifGhSIH7cH07OOCXd2C8Hg'
      }

      headers = {
          'Content-Type': 'application/x-www-form-urlencoded'
      }

      try:
          response = requests.post(url, data=data, headers=headers)
          response.raise_for_status()

          token_data = response.json()
          access_token = token_data['access_token']
          expires_in = token_data['expires_in']

          # Calcula horário de expiração
          expiration_time = datetime.now() + timedelta(seconds=expires_in)

          print(f"✓ Token obtido com sucesso!")
          print(f"Expira em: {expiration_time.strftime('%Y-%m-%d %H:%M:%S')}")

          return access_token, expiration_time

      except requests.exceptions.RequestException as e:
          print(f"✗ Erro ao obter token: {e}")
          raise

  # Uso
  if __name__ == "__main__":
      token, expires_at = get_access_token()
      print(f"\nToken: {token[:50]}...")
  ```

  ```java Java theme={"theme":"catppuccin-latte"}
  import java.net.http.*;
  import java.net.*;
  import java.time.*;
  import com.google.gson.*;

  public class FlexdocAuthClient {

      private static final String TOKEN_URL =
          "https://auth-des.flexdoc-apis.com.br/realms/fleximage/protocol/openid-connect/token";
      private static final String CLIENT_ID = "teste-api";
      private static final String CLIENT_SECRET = "pwR8GPipQvifGhSIH7cH07OOCXd2C8Hg";

      public static class TokenResponse {
          public String access_token;
          public String token_type;
          public int expires_in;
          public String scope;
      }

      public static TokenResponse getAccessToken() throws Exception {
          HttpClient client = HttpClient.newHttpClient();

          String body = "grant_type=client_credentials" +
                        "&client_id=" + URLEncoder.encode(CLIENT_ID, "UTF-8") +
                        "&client_secret=" + URLEncoder.encode(CLIENT_SECRET, "UTF-8");

          HttpRequest request = HttpRequest.newBuilder()
              .uri(URI.create(TOKEN_URL))
              .header("Content-Type", "application/x-www-form-urlencoded")
              .POST(HttpRequest.BodyPublishers.ofString(body))
              .build();

          HttpResponse<String> response = client.send(request,
              HttpResponse.BodyHandlers.ofString());

          if (response.statusCode() == 200) {
              Gson gson = new Gson();
              TokenResponse tokenResponse = gson.fromJson(
                  response.body(),
                  TokenResponse.class
              );

              Instant expiresAt = Instant.now().plusSeconds(tokenResponse.expires_in);
              System.out.println("✓ Token obtido com sucesso!");
              System.out.println("Expira em: " + expiresAt);

              return tokenResponse;
          } else {
              throw new RuntimeException("Erro ao obter token: " + response.body());
          }
      }

      public static void main(String[] args) {
          try {
              TokenResponse token = getAccessToken();
              System.out.println("\nToken: " +
                  token.access_token.substring(0, 50) + "...");
          } catch (Exception e) {
              System.err.println("Erro: " + e.getMessage());
          }
      }
  }
  ```

  ```php PHP theme={"theme":"catppuccin-latte"}
  <?php

  function getAccessToken() {
      $url = 'https://auth-des.flexdoc-apis.com.br/realms/fleximage/protocol/openid-connect/token';

      $data = [
          'grant_type' => 'client_credentials',
          'client_id' => 'teste-api',
          'client_secret' => 'pwR8GPipQvifGhSIH7cH07OOCXd2C8Hg'
      ];

      $ch = curl_init($url);
      curl_setopt($ch, CURLOPT_POST, true);
      curl_setopt($ch, CURLOPT_POSTFIELDS, http_build_query($data));
      curl_setopt($ch, CURLOPT_RETURNTRANSFER, true);
      curl_setopt($ch, CURLOPT_HTTPHEADER, [
          'Content-Type: application/x-www-form-urlencoded'
      ]);

      $response = curl_exec($ch);
      $httpCode = curl_getinfo($ch, CURLINFO_HTTP_CODE);
      curl_close($ch);

      if ($httpCode === 200) {
          $tokenData = json_decode($response, true);
          $expiresAt = date('Y-m-d H:i:s', time() + $tokenData['expires_in']);

          echo "✓ Token obtido com sucesso!\n";
          echo "Expira em: {$expiresAt}\n";

          return $tokenData['access_token'];
      } else {
          throw new Exception("Erro ao obter token: {$response}");
      }
  }

  // Uso
  try {
      $token = getAccessToken();
      echo "\nToken: " . substr($token, 0, 50) . "...\n";
  } catch (Exception $e) {
      echo "Erro: " . $e->getMessage() . "\n";
  }
  ?>
  ```

  ```go Go theme={"theme":"catppuccin-latte"}
  package main

  import (
      "encoding/json"
      "fmt"
      "io"
      "net/http"
      "net/url"
      "strings"
      "time"
  )

  type TokenResponse struct {
      AccessToken      string `json:"access_token"`
      TokenType        string `json:"token_type"`
      ExpiresIn        int    `json:"expires_in"`
      RefreshExpiresIn int    `json:"refresh_expires_in"`
      Scope            string `json:"scope"`
  }

  func getAccessToken() (*TokenResponse, error) {
      tokenURL := "https://auth-des.flexdoc-apis.com.br/realms/fleximage/protocol/openid-connect/token"

      data := url.Values{}
      data.Set("grant_type", "client_credentials")
      data.Set("client_id", "teste-api")
      data.Set("client_secret", "pwR8GPipQvifGhSIH7cH07OOCXd2C8Hg")

      req, err := http.NewRequest("POST", tokenURL, strings.NewReader(data.Encode()))
      if err != nil {
          return nil, err
      }

      req.Header.Set("Content-Type", "application/x-www-form-urlencoded")

      client := &http.Client{Timeout: 10 * time.Second}
      resp, err := client.Do(req)
      if err != nil {
          return nil, err
      }
      defer resp.Body.Close()

      body, err := io.ReadAll(resp.Body)
      if err != nil {
          return nil, err
      }

      if resp.StatusCode != http.StatusOK {
          return nil, fmt.Errorf("erro ao obter token: %s", string(body))
      }

      var tokenResp TokenResponse
      if err := json.Unmarshal(body, &tokenResp); err != nil {
          return nil, err
      }

      expiresAt := time.Now().Add(time.Duration(tokenResp.ExpiresIn) * time.Second)
      fmt.Printf("✓ Token obtido com sucesso!\n")
      fmt.Printf("Expira em: %s\n", expiresAt.Format("2006-01-02 15:04:05"))

      return &tokenResp, nil
  }

  func main() {
      tokenResp, err := getAccessToken()
      if err != nil {
          fmt.Printf("Erro: %v\n", err)
          return
      }

      fmt.Printf("\nToken: %s...\n", tokenResp.AccessToken[:50])
  }
  ```
</CodeGroup>

***

## Gerenciamento de Token

<AccordionGroup>
  <Accordion title="Tempo de Validade" icon="clock">
    O token expira em **1 hora (3600 segundos)** após sua geração.

    **Recomendações:**

    * Armazene o horário de expiração ao obter o token
    * Renove o token **5 minutos antes** de expirar (margem de segurança)
    * Implemente renovação automática em caso de erro 401

    **Exemplo de validação:**

    ```javascript theme={"theme":"catppuccin-latte"}
    class TokenManager {
      constructor() {
        this.token = null;
        this.expiresAt = null;
      }

      async getToken() {
        // Se token ainda é válido (com margem de 5min)
        if (this.token && this.expiresAt > Date.now() + 300000) {
          return this.token;
        }

        // Renova o token
        const response = await this.fetchNewToken();
        this.token = response.access_token;
        this.expiresAt = Date.now() + (response.expires_in * 1000);

        return this.token;
      }
    }
    ```
  </Accordion>

  <Accordion title="Cache de Token" icon="database">
    Para otimizar performance e reduzir requisições desnecessárias:

    ✅ **Faça:**

    * Cache o token em memória durante sua validade
    * Use variáveis de ambiente para credenciais
    * Implemente singleton/padrão de instância única

    ❌ **Não faça:**

    * Armazenar token em localStorage (frontend)
    * Compartilhar tokens entre ambientes diferentes
    * Fazer uma requisição de auth para cada chamada à API
  </Accordion>

  <Accordion title="Tratamento de Erros" icon="triangle-exclamation">
    **Erro 401 - Invalid Client**

    ```json theme={"theme":"catppuccin-latte"}
    {
      "error": "invalid_client",
      "error_description": "Invalid client credentials"
    }
    ```

    **Causa:** Client ID ou Client Secret incorretos

    **Solução:** Verifique suas credenciais

    ***

    **Erro 400 - Invalid Request**

    ```json theme={"theme":"catppuccin-latte"}
    {
      "error": "invalid_request",
      "error_description": "Missing form parameter: grant_type"
    }
    ```

    **Causa:** Parâmetros obrigatórios ausentes

    **Solução:** Certifique-se de enviar `grant_type`, `client_id` e `client_secret`

    ***

    **Erro 500 - Server Error**

    **Causa:** Erro interno no servidor de autenticação

    **Solução:** Implemente retry com backoff exponencial (aguarde 1s, 2s, 4s entre tentativas)
  </Accordion>

  <Accordion title="Segurança" icon="shield">
    🔒 **Boas Práticas de Segurança:**

    1. **Nunca exponha credenciais:**
       * Não commite client\_secret em repositórios Git
       * Use variáveis de ambiente (.env)
       * Adicione .env ao .gitignore
    2. **Proteja o token:**
       * Nunca envie tokens em URLs (query params)
       * Use apenas HTTPS em produção
       * Não armazene em cookies sem httpOnly/secure flags
    3. **Rotação de credenciais:**
       * Rotacione secrets a cada 90 dias
       * Mantenha credenciais antigas ativas por 7 dias durante transição
    4. **Monitoramento:**
       * Registre tentativas de autenticação falhas
       * Configure alertas para uso anômalo
       * Audite uso de tokens periodicamente
  </Accordion>
</AccordionGroup>

***

## Ambientes

<Tabs>
  <Tab title="Desenvolvimento (DEV)">
    ### Endpoint

    ```text theme={"theme":"catppuccin-latte"}
    POST https://auth-des.flexdoc-apis.com.br/realms/fleximage/protocol/openid-connect/token
    ```

    ### Credenciais de Teste

    | Parâmetro         | Valor                              |
    | ----------------- | ---------------------------------- |
    | **Client ID**     | `teste-api`                        |
    | **Client Secret** | `pwR8GPipQvifGhSIH7cH07OOCXd2C8Hg` |
    | **Realm**         | `fleximage`                        |

    <Info>
      Use estas credenciais para testes e homologação. Não há limite de requisições no ambiente DEV.
    </Info>
  </Tab>

  <Tab title="Produção (PROD)">
    ### Endpoint

    ```text theme={"theme":"catppuccin-latte"}
    POST https://auth.flexdoc-apis.com.br/realms/fleximage/protocol/openid-connect/token
    ```

    ### Credenciais

    <Warning>
      🔒 Credenciais de produção são fornecidas individualmente após homologação completa.

      Para solicitar acesso, envie email para: [**integracao@flexdoc.com.br**](mailto:integracao@flexdoc.com.br)
    </Warning>

    ### Rate Limits

    * **Autenticação:** 60 requisições/minuto
    * **APIs:** 1000 requisições/minuto (com token válido)

    <Info>
      Implementamos rate limiting para proteção do serviço. Se exceder o limite, receberá HTTP 429 (Too Many Requests).
    </Info>
  </Tab>
</Tabs>

***

## Próximos Passos

Após obter seu token de acesso, você está pronto para usar as APIs do Flexdoc!

<CardGroup cols={2}>
  <Card title="Importação de Documentos" icon="file-arrow-up" href="/import-doc-fleximage">
    Envie documentos para análise e validação automática
  </Card>
</CardGroup>

***
