iDigital OIDC Provider (OAuth 2.0 + OpenID Connect Provider)

Serviço de autenticação e autorização para a framework iDigital do IFAP.

O OpenID Connect (OIDC) é um layer de autenticação sobre a framework de autorização OAuth 2.0.

Endpoints

Estão disponíveis os seguintes endpoints:

O <server_base_url> irá depender do ambiente (desenvolvimento, testes ou produção).

1. Authentication

O Authentication Request deverá ser feito usando o Authorization Code Flow como especificado na secção 4.1 do OAuth 2.0 e secção 3.1.2.1 do OpeniID Connect.

O Authorization Code Flow retorna um Authorization Code ao cliente, que depois pode ser trocado por um ID Token e Access Token diretamente.

1.1. Authorization Request

O cliente deve construir o URI indicando os seguintes parâmetros:

Para além destes scopes podem também ser indicados códigos de perfis do iDigital (identificador numérico). Na resposta serão devolvidos no scope apenas os códigos destes perfis que estão atribuídos ao utilizador.

[v1.6.0] Também podem ser indicadas permissões a menus no formato 1111.ABCDEFG:permissão ou só ABCDEFG:permissão, onde 1111 é a campanha do menu (não obrigatório para menus do iDigital, se não for indicado é assumido 1111), ABCDEFG é o código do menu e permissão` pode ser os valores “insert”/“create”, “query”/“read”, “update” ou “delete”. Na resposta serão devolvidos apenas as permissões que o utilizador tenha. Por exemplo, se o utilizador só tem permissão de “query” para o menu ABC se indicar no authorization request um scope “openid ABC:insert ABC:query ABC:update ABC:delete” na resposta o scope só irá conter “openid ABC:query”.

Exemplo de um authorization request:

https://xxxx.ifap.pt/idigital-oauth2/api/authorization?
    response_type=code
    &client_id=123456789
    &redirect_uri=https://dummy.site.pt/authorization-response
    &scope=openid%20name%20email%20uti_nif%20uti_tipo
    &state=qwertyuiop1234567890

1.2. Authorization Code Response

Se o request estiver válido e o utilizador der a sua autorização, o authorization server gera um Authorization Code e redireciona o utilizador novamente para a aplicação, adicionando o “code” e o “state” ao redirect URI.

Exemplo de uma resposta:

HTTP/1.1 302 Found
Location: https://dummy.site.pt/authorization-response?
    code=DUMMY-CODE
    &state=qwertyuiop1234567890

1.3. Resposta com erro

Existem dois casos em que o servidor deve mostrar uma mensagem de erro diretamente em vez de redirecionar o utilizador para a aplicação: se o client_id for inválido ou se o redirect_uri for inválido. Nos restantes casos pode-se redirecionar o utilizador para o endereço de redirect da aplicação juntamente com parâmetros de query string descrevendo o erro.

Ao redirecionar para a aplicação o servidor adiciona os seguintes parâmetros ao redirect URL:

error

Pode conter um dos seguintes valores:

error_description

O servidor pode opcionalmente incluir uma descrição do erro neste parâmetro. Este parâmetro é destinado ao developer para este perceber melhor a situação e não deve ser mostrado ao utilizador.

error_uri

O servidor também pode retornar um URL para uma página com informação acerca do erro.

Exemplo

HTTP/1.1 302 Found
Location: https://dummy.site.pt/authorization-response?
    error=access_denied
    &error_description=The+user+denied+the+request
    &error_uri=https%3A%2F%2Ftools.ietf.org%2Fhtml%2Frfc6749
    &state=qwertyuiop1234567890

2. Token Request

Para obter um Access Token, um ID Token e um Refresh Token o cliente deve enviar um Token Request para o Token Endpoint:

POST /idigital-oauth2/api/token HTTP/1.1
Host: xxxx.ifap.pt
Content-Type: application/x-www-form-urlencoded

grant_type=authorization_code
  &code=DUMMY-CODE
  &client_id=123456789
  &client_secret=QWERTYUIOP
  &redirect_uri=https%3A%2F%2Fdummy.site.pt%2Ftoken-response

2.1. Resposta com Sucesso

Uma resposta com sucesso pode ser algo como:

HTTP/1.1 200 OK
Content-Type: application/json
Cache-Control: no-store
Pragma: no-cache
 
{
    "scope": "openid name email uti_nif uti_tipo",
    "state": "qwertyuiop1234567890",
    "id_token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJqdGkiOiI1ZDA4YzQyZS0wNTZjLTQwZTMtYWY0OS01ZTRlOTIwZWUyYzciLCJpYXQiOjE1MjYwNTA3MjgsImlzcyI6ImxvY2FsaG9zdCIsInRva2VuX3R5cGUiOiJpZF90b2tlbiIsInNjb3BlIjoib3BlbmlkIG5hbWUgZW1haWwgdXRpX25pZiB1dGlfdGlwbyIsImF1ZCI6IjQxMWU4YjlkLTVjMjAtNDc2Yy1iZGU1LWNlZWJlMDY0YmVmNSIsInN1YiI6ImR1bW15dXNlciIsIm5hbWUiOiJEdW1teSBVc2VyIE5hbWUiLCJlbWFpbCI6ImR1bW15LnVzZXJAaWZhcC5wdCIsInV0aV9uaWYiOiIxMjM0NTY3ODkiLCJ1dGlfdGlwbyI6IkkiLCJleHAiOjE1MjYwNTEwMjh9.g0Uvm-HxHug_zx-ZjiYmsApP5fw4N8WEEDMCLq7kRcG0jwbwtRHY15GAZDa1JIR5NskA4LxeIFo26jjVZ-zJVQ",
    "access_token": "qwertyuiopasdfghjklzxcvbnm",
    "token_type": "bearer",
    "expires_in": 1526050785886,
    "refresh_token": "mnbvcxzlkjhgfdsapoiuytrewq"
}

O id_token é um JSON Web Token (JWT) e destina-se a ser usado diretamente pelo cliente para obter informação acerca do utilizador.

O token está assinado com o algoritmo HS512 usando como chave o client_secret. O cliente deve verificar o token conforme especificado na secção 3.1.3.7. do OpenID Connect

Um exemplo de ID Token:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzUxMiJ9.eyJqdGkiOiI1ZDA4YzQyZS0wNTZjLTQwZTMtYWY0OS01ZTRlOTIwZWUyYzciLCJpYXQiOjE1MjYwNTA3MjgsImlzcyI6ImxvY2FsaG9zdCIsInRva2VuX3R5cGUiOiJpZF90b2tlbiIsInNjb3BlIjoib3BlbmlkIG5hbWUgZW1haWwgdXRpX25pZiB1dGlfdGlwbyIsImF1ZCI6IjQxMWU4YjlkLTVjMjAtNDc2Yy1iZGU1LWNlZWJlMDY0YmVmNSIsInN1YiI6ImR1bW15dXNlciIsIm5hbWUiOiJEdW1teSBVc2VyIE5hbWUiLCJlbWFpbCI6ImR1bW15LnVzZXJAaWZhcC5wdCIsInV0aV9uaWYiOiIxMjM0NTY3ODkiLCJ1dGlfdGlwbyI6IkkiLCJleHAiOjE1MjYwNTEwMjh9.g0Uvm-HxHug_zx-ZjiYmsApP5fw4N8WEEDMCLq7kRcG0jwbwtRHY15GAZDa1JIR5NskA4LxeIFo26jjVZ-zJVQ

Depois de descodificado o token contém o header:

{
  "typ": "JWT",
  "alg": "HS512"
}

E o payload:

{
  "jti": "5d08c42e-056c-40e3-af49-5e4e920ee2c7",
  "iat": 1526050728,
  "iss": "localhost",
  "token_type": "id_token",
  "scope": "openid name email uti_nif uti_tipo",
  "aud": "411e8b9d-5c20-476c-bde5-ceebe064bef5",
  "sub": "dummyuser",
  "name": "Dummy User Name",
  "email": "dummy.user@ifap.pt",
  "uti_nif": "123456789",
  "uti_tipo": "I",
  "exp": 1526051028
}

Para verificar o conteúdo de um JWT pode ser usado o site https://jwt.io/ .

2.2. Resposta com Erro

HTTP/1.1 400 Bad Request
Content-Type: application/json;charset=UTF-8
Cache-Control: no-store
Pragma: no-cache
 
{
  "error": "invalid_request",
  "error_description": "Request was missing the 'redirect_uri' parameter.",
  "error_uri": "https://tools.ietf.org/html/rfc6749"
}

3. Atualizar um Access Token

Para atualizar um Access Token o cliente tem de fazer um request ao token endpoint com os seguintes parâmetros:

POST /idigital-oauth2/api/token HTTP/1.1
Host: xxxx.ifap.pt
Content-Type: application/x-www-form-urlencoded

grant_type=refresh_token
  &refresh_token=mnbvcxzlkjhgfdsapoiuytrewq

Opcionalmente também pode ser incluído o parâmetro scope que, no caso de ser incluído, não pode conter nenhum scope que não tenha sido atribuído no token original. No caso de não ser indicado é tratado como sendo o mesmo do token original.

4. Token Introspection

Quando um cliente OAuth 2.0 faz um request a um resource server, este precisa de verificar que o Access Token é válido. A especificação do OAuth 2.0 não define um método específico para verificar Access Tokens, apenas menciona que esta verificação deve ser coordenada entre as partes. Em alguns casos, nomeadamente em serviços locais, ambos os endpoints fazem parte do mesmo sistema e podem partilhar informação necessária para fazer estas validações através da base de dados, por exemplo. Em sistemas maiores em que os endpoints fazem parte de sistemas diferentes é necessário um método diferente para fazer esta verificação.

O RFC7662 - OAuth 2.0 Token Introspection define um protocolo que retorna informação acerca de um Access Token e destina-se a ser usado por resource servers.

4.1. Token Introspection Request

O request é feito por POST contendo o parâmetro “token” e a autenticação do cliente. Os parâmetros são enviados no body por “application/x-www-form-urlencoded”, como definido em W3C.REC-html5-20141028.

POST /idigital-oauth2/api/introspect HTTP/1.1
Host: xxxx.ifap.pt
Content-Type: application/x-www-form-urlencoded

token=qwertyuiopasdfghjklzxcvbnm
  &client_id=123456789
  &client_secret=QWERTYUIOP

4.2. Token Introspection Response

O Token Introspection Endpoint responde com um objeto JSON com várias propriedades.

No caso de o token estar válido e ativo a resposta será:

{
    "active": true,
    "scope": "openid profile",
    "client_id": 123456789,
    "sub": "dummyuser",
    "sid": "999999999",
    "username": "Dummy User Name",
    "exp": 1582904531000,
    "state": "qwertyiop1234567890"
}

Se o token não estiver ativo a resposta será:

{
    "active": false,
    "scope": mull,
    "client_id": null,
    "sub": null,
    "username": null,
    "exp": null,
    "state": "qwertyiop1234567890"
}

5. Logout

5.1. Logout Request

O logout request termina a sessão e invalida todos os tokens que tenham sido gerados a todos os clientes. Normalmente os clientes não devem invocar este endpoint porque só precisam de fazer o logout da sua aplicação.

https://xxxx.ifap.pt/idigital-oauth2/api/logout?
    id_token_hint=eyJ0eXAiOiJKV1Qi ... zJVQ
    &post_logout_redirect_uri=https://dummy.site.pt/logout
    &state=qwertyuiop1234567890

Nenhum parâmero é obrigatório. No entanto no caso de não ser fornecido um id_token_hint o post_logout_redirect_uri só pode ter endereços do domínio ou subdomínios ifap.pt (por exemplo https://exemplo.ifap.pt/qualquer-coisa).

5.2. Logout Response

Se o request estiver válido, o authorization server termina a sessão e redireciona o utilizador novamente para a aplicação, eliminando o session cookie e acrescentando o “state” ao redirect URI.

Exemplo de uma resposta:

HTTP/1.1 302 Found
Location: https://dummy.site.pt/logout?
    state=qwertyuiop1234567890

6. User Info

5.1. User Info Request

O cliente envia o pedido usando HTTP GET ou HTTP POST. DEVE ser enviado um bearer token com o access_token obtido de uma solicitação de autenticação do OpenID Connect, conforme definido na Seção 2 do Uso de bearer tokens do OAuth 2.0 [RFC6750].

É RECOMENDADO que o pedido seja feito com o método HTTP GET e o access_token seja enviado no campo Authorization do header.

GET /idigital-oauth2/api/userinfo HTTP/1.1
Host: xxxx.ifap.pt
Authorization: Bearer qwertyuiopasdfghjklzxcvbnm

5.2. User Info Response

Se o pedido estiver correto a resposta é devolvida em JSON:

{
    "sub": "dummyuser",
    "name": "Dummy User Name",
    "email": "dummyuser@ifap.pt",
    "uti_tipo": "I",
    "uti_nif": null,
    "uor_cod_uni_org": "IFAP00"
}

Os atributos devolvidos vão depender do scope do token enviado.


Copyright (c) 2023, IFAP. Todos os direitos reservados.