

As traduções são geradas por tradução automática. Em caso de conflito entre o conteúdo da tradução e da versão original em inglês, a versão em inglês prevalecerá.

# Autenticação OIDC para DICOMweb APIs
<a name="dicomweb-oidc"></a>

A AWS HealthImaging oferece suporte à autenticação baseada em [OAuth 2.0](https://oauth.net/2/) para solicitações de DICOMweb API usando o [OpenID Connect (OIDC)](https://openid.net/specs/openid-connect-core-1_0.html), além da autenticação [AWS Signature Version 4 (SigV4](https://docs.aws.amazon.com/AmazonS3/latest/API/sig-v4-authenticating-requests.html)) existente. O OIDC permite que você se integre HealthImaging diretamente com provedores de identidade externos (IdPs) e permite que você forneça aos aplicativos baseados em padrões acesso aos seus dados de imagens médicas por meio de HealthImaging DICOMweb terminais, sem exigir que cada aplicativo tenha credenciais. AWS 

**Topics**
+ [Verificação personalizada de tokens com autorizadores Lambda](dicomweb-oidc-how.md)
+ [Configurar um autorizador AWS Lambda para autenticação OIDC](dicomweb-oidc-requirements.md)

# Verificação personalizada de tokens com autorizadores Lambda
<a name="dicomweb-oidc-how"></a>

HealthImaging implementa o suporte do OIDC por meio de uma arquitetura que usa autorizadores Lambda, permitindo que os clientes implementem sua própria lógica de verificação de token. Esse design oferece controle flexível sobre como os tokens são validados e como as decisões de acesso são aplicadas, acomodando um cenário diversificado de provedores de identidade compatíveis com OIDC (IdPs) e vários métodos de verificação de tokens.

## Fluxo de autenticação
<a name="dicomweb-oidc-authentication-flow"></a>

Veja como a autenticação funciona em alto nível:

1. O **cliente chama a DICOMweb API:** seu aplicativo se autentica com o provedor de identidade OIDC escolhido e recebe um token de ID assinado (JWT). Para cada solicitação DICOMweb HTTP, o cliente deve incluir o token de acesso OIDC no cabeçalho de autorização (normalmente um token de portador). Antes que a solicitação chegue aos seus dados, HealthImaging extrai esse token da solicitação recebida e chama um autorizador Lambda que você configura.

   1. O cabeçalho normalmente segue o formato:`Authorization: Bearer <token>`.

1. **Verificação inicial:** HealthImaging verifica as reivindicações do token de acesso para rejeitar rapidamente quaisquer tokens obviamente inválidos ou expirados sem invocar a função Lambda desnecessariamente. HealthImaging executa uma verificação inicial de determinadas declarações padrão no token de acesso antes de invocar o autorizador Lambda:

   1. `iat`(Emitido em): HealthImaging verifica se o tempo de emissão do token está dentro dos limites aceitáveis.

   1. `exp`(Tempo de expiração): HealthImaging verifica se o token não expirou.

   1. `nbf`(Não antes da hora): Se presente, HealthImaging garante que o token não esteja sendo usado antes do horário de início válido.

1. **HealthImaging invoca um autorizador do Lambda:** se a verificação inicial da solicitação for aprovada, HealthImaging delegará a verificação adicional do token à função autorizadora do Lambda configurada pelo cliente. HealthImaging passa o token extraído e outras informações relevantes da solicitação para a função Lambda. A função Lambda verifica a assinatura e as declarações do token.

1. **Verifique com um provedor de identidade:** o Lambda contém um código personalizado que verifica a assinatura do token de ID, realiza uma verificação mais abrangente do token (por exemplo, emissor, público, declarações personalizadas) e valida essas declarações em relação ao IdP quando necessário.

1. **O autorizador retorna uma política de acesso:** após a verificação bem-sucedida, a função Lambda determina as permissões apropriadas para o uso autenticado. Em seguida, o autorizador do Lambda retorna o nome do recurso da Amazon (ARN) de uma função do IAM que representa o conjunto de permissões a serem concedidas.

1. **Execução da solicitação:** se a função assumida do IAM tiver as permissões necessárias, HealthImaging continue com a devolução do DICOMWeb recurso solicitado. Se as permissões forem insuficientes, HealthImaging nega a solicitação e retorna um erro de resposta de erro apropriado (ou seja, 403 Forbidden).

**nota**  
A função lambda do autorizador não é gerenciada pelo serviço da AWS HealthImaging . Ele é executado em sua AWS conta. Os clientes são cobrados pela invocação da função e pelo tempo de execução separadamente de suas HealthImaging cobranças.

## Visão geral da arquitetura
<a name="dicomweb-oidc-architecture-overview"></a>

![\[Diagrama mostrando o fluxo de trabalho: o cliente envia o token, o autorizador Lambda valida, processa a solicitação HealthImaging\]](http://docs.aws.amazon.com/pt_br/healthimaging/latest/devguide/images/security-oidc-workflow-lambda.png)


## Pré-requisitos
<a name="dicomweb-oidc-prerequisites"></a>

### Requisitos de token de acesso
<a name="dicomweb-oidc-token-requirements"></a>

HealthImaging exige que o token de acesso esteja no formato JSON Web Token (JWT). Muitos provedores de identidade (IDPs) oferecem esse formato de token nativamente, enquanto outros permitem que você selecione ou configure o formulário do token de acesso. Certifique-se de que seu IDP escolhido possa emitir tokens JWT antes de prosseguir com a integração.

Formato do token  
O token de acesso deve estar no formato JWT (JSON Web Token)

Declarações necessárias    
`exp`(Tempo de expiração)  
Reivindicação obrigatória que especifica quando o token se torna inválido.  
+ Deve ser posterior à hora atual em UTC
+ Representa quando o token se torna inválido  
`iat`(Emitido em)  
Reivindicação obrigatória que especifica quando o token foi emitido.  
+ Deve ser antes da hora atual em UTC
+ NÃO deve ser antes de 12 horas antes da hora atual em UTC
+ Isso efetivamente impõe uma vida útil máxima do token de 12 horas.  
`nbf`(Não antes do tempo)  
Declaração opcional que especifica a primeira vez em que o token pode ser usado.  
+ Se presente, será avaliado por HealthImaging
+ Especifica o tempo antes do qual o token não deve ser aceito

### Requisitos de tempo de resposta do autorizador Lambda
<a name="dicomweb-oidc-lambda"></a>

HealthImaging impõe requisitos rígidos de tempo para as respostas do autorizador Lambda para garantir o desempenho ideal da API. Sua função Lambda **deve** retornar em 1 segundo.

## Práticas recomendadas
<a name="dicomweb-oidc-best-practices"></a>

### Otimize a verificação de tokens
<a name="dicomweb-oidc-optimization"></a>
+ Armazene JWKS (JSON Web Key Sets) quando possível
+ Armazene tokens de acesso válidos em cache quando possível
+ Minimize as chamadas de rede para seu provedor de identidade
+ Implemente uma lógica eficiente de validação de tokens

### Configuração Lambda
<a name="dicomweb-oidc-lambda-configuration"></a>
+ As funções baseadas em Python e Node.js normalmente inicializam mais rápido
+ Reduza a quantidade de bibliotecas externas a serem carregadas
+ Configure a alocação de memória adequada para garantir um desempenho consistente
+ Monitore os tempos de execução usando CloudWatch métricas

## Ativação da autenticação OIDC
<a name="dicomweb-oidc-enablement"></a>
+ **A autenticação OIDC **só** pode ser ativada ao criar um novo armazenamento de dados**
+ A ativação do OIDC para armazenamentos de dados existentes não é suportada pela API
+ Para habilitar o OIDC em um armazenamento de dados existente, os clientes devem entrar em contato com o Support AWS 

# Configurar um autorizador AWS Lambda para autenticação OIDC
<a name="dicomweb-oidc-requirements"></a>

Este guia pressupõe que você já tenha configurado o provedor de identidade (IdP) de sua escolha para fornecer tokens de acesso compatíveis com os requisitos do recurso de autenticação HealthImaging do OIDC.

## 1. Configurar funções do IAM para acesso à DICOMWeb API
<a name="dicomweb-oidc-iam-roles"></a>

Antes de configurar o autorizador Lambda, crie funções do IAM HealthImaging para assumir ao DICOMWeb processar solicitações de API. A função autorizadora Lambda retorna uma dessas funções ARN após a verificação bem-sucedida do token, HealthImaging permitindo executar as solicitações com as permissões apropriadas.

1. Crie políticas do IAM definindo os privilégios de DICOMWeb API desejados. Consulte a seção "[Usando DICOMweb](https://docs.aws.amazon.com/healthimaging/latest/devguide/using-dicomweb.html)" da HealthImaging documentação para ver as permissões disponíveis.

1. Crie funções do IAM que:
   + Anexe essas políticas
   + Inclua uma relação de confiança que permita que o diretor HealthImaging de serviço da AWS (`medical-imaging.amazonaws.com`) assuma essas funções.

Aqui está um exemplo de uma política que permite que funções associadas acessem a API HealthImaging DICOMWeb somente para leitura:

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "MedicalImagingDicomWebOperations",
            "Effect": "Allow",
            "Action": [
                "medical-imaging:SearchDICOMInstances",
                "medical-imaging:GetImageSetMetadata",
                "medical-imaging:GetDICOMSeriesMetadata",
                "medical-imaging:SearchDICOMStudies",
                "medical-imaging:GetDICOMBulkdata",
                "medical-imaging:SearchDICOMSeries",
                "medical-imaging:GetDICOMInstanceMetadata",
                "medical-imaging:GetDICOMInstance",
                "medical-imaging:GetDICOMInstanceFrames"
            ],
            "Resource": "arn:aws:medical-imaging:us-east-1:123456789012:datastore/datastore-123"
        }
    ]
}
```

------

Aqui está um exemplo da política de relacionamento de confiança que deve ser associada à (s) função (s):

------
#### [ JSON ]

****  

```
{
    "Version":"2012-10-17",		 	 	 
    "Statement": [
        {
            "Sid": "OIDCRoleFederation",
            "Effect": "Allow",
            "Principal": {
                "Service": "medical-imaging.amazonaws.com"
        },
            "Action": "sts:AssumeRole"
        }
    ]
}
```

------

O autorizador Lambda que você criará na próxima etapa pode avaliar as reivindicações de token e retornar o ARN da função apropriada. A AWS então HealthImaging representará essa função para executar a solicitação de DICOMWeb API com as permissões correspondentes.

Por exemplo:
+ Um token com declarações de “administrador” pode retornar um ARN para uma função com acesso total
+ Um token com declarações de “leitor” pode retornar um ARN para uma função com acesso somente de leitura
+ Um token com declarações “Department\$1A” pode retornar um ARN para uma função específica do nível de acesso desse departamento

Esse mecanismo permite que você mapeie o modelo de autorização do seu IdP para HealthImaging permissões específicas da AWS por meio de funções do IAM.

## 2. Criar e configurar a função Lambda Authorizer
<a name="dicomweb-oidc-configure-lambda"></a>

Crie uma função Lambda que verificará o token JWT e retornará o ARN apropriado da função do IAM com base na avaliação das declarações do token. Essa função é invocada pelo serviço de imagens de saúde e transmitida por um evento que contém o ID do HealthImaging armazenamento de dados, a DICOMWeb operação e o token de acesso encontrados na solicitação HTTP:

```
{
  "datastoreId": "{datastore id}",
  "operation": "{Healthimaging API name e.g. GetDICOMInstance}",
  "bearerToken": "{access token}"
}
```

A função autorizadora do Lambda deve retornar uma resposta JSON com a seguinte estrutura:

```
{
  "isTokenValid": {true or false},
  "roleArn": "{role arn or empty string meaning to deny the request explicitly}"
}
```

Você pode consultar o exemplo de implementação para obter mais informações.

**nota**  
Como a DICOMWeb solicitação só é respondida depois que o token de acesso é verificado pelo autorizador lambda, é importante que a execução dessa função seja a mais rápida possível para fornecer o melhor tempo de resposta da DICOMWeb API.

Para que o HealthImaging serviço seja autorizado a invocar a função autorizadora lambda, ele deve ter uma política de recursos que permita que o HealthImaging serviço a invoque. Essa política de recursos pode ser criada no menu de permissões da guia de configuração lambda ou usando AWS CLI:

```
aws lambda add-permission \
    --function-name YourAuthorizerFunctionName \
    --statement-id HealthImagingInvoke \
    --action lambda:InvokeFunction \
    --principal medical-imaging.amazonaws.com
```

Essa política de recursos permite que o HealthImaging serviço invoque seu autorizador Lambda ao DICOMWeb autenticar solicitações de API.

**nota**  
A política de recursos lambda pode ser atualizada posteriormente com uma condição "ArnLike" correspondente ao ARN de um HealthImaging armazenamento de dados específico.

Aqui está um exemplo da política de recursos lambda:

------
#### [ JSON ]

****  

```
{
  "Version":"2012-10-17",		 	 	 
  "Id": "default",
  "Statement": [
    {
      "Sid": "LambaAuthorizer-HealthImagingInvokePermission",
      "Effect": "Allow",
      "Principal": {
        "Service": "medical-imaging.amazonaws.com"
      },
      "Action": "lambda:InvokeFunction",
      "Resource": "arn:aws:lambda:us-east-1:123456789012::function:{LambdaAuthorizerFunctionName}",
      "Condition": {
        "ArnLike": {
          "AWS:SourceArn": "arn:aws:medical-imaging:us-east-1:123456789012:datastore/datastore-123"
        }
      }
    }
  ]
}
```

------

## 3. Crie um novo armazenamento de dados com a autenticação OIDC
<a name="dicomweb-oidc-datastore"></a>

Para habilitar a autenticação OIDC, você deve criar um novo armazenamento de dados usando o AWS CLI com o parâmetro "”. lambda-authorizer-arn A autenticação OIDC não pode ser habilitada nos armazenamentos de dados existentes sem entrar em contato com o Support. AWS 

Veja um exemplo de como criar um novo armazenamento de dados com a autenticação OIDC ativada:

```
aws medical-imaging create-datastore \
    --datastore-name YourDatastoreName \
    --lambda-authorizer-arn YourAuthorizerFunctionArn
```

Você pode verificar se um armazenamento de dados específico tem o recurso de autenticação OIDC ativado usando o comando AWS CLI get-datastore e verificando se o atributo "" está presente: lambdaAuthorizerArn

```
aws medical-imaging get-datastore --datastore-id YourDatastoreId
```

```
{
    "datastoreProperties": {
        "datastoreId": YourdatastoreId,
        "datastoreName": YourDatastoreName,
        "datastoreStatus": "ACTIVE",
        "lambdaAuthorizerArn": YourAuthorizerFunctionArn,
        "datastoreArn": YourDatastoreArn,
        "createdAt": "2025-09-30T14:16:04.015000-05:00",
        "updatedAt": "2025-09-30T14:16:04.015000-05:00"
    }
}
```

**nota**  
A função de execução do comando de criação do AWS CLI armazenamento de dados deve ter as permissões apropriadas para invocar a função autorizadora do Lambda. Isso atenua os ataques de escalonamento de privilégios em que usuários mal-intencionados podem executar funções não autorizadas do Lambda por meio da configuração do autorizador do armazenamento de dados.

## Códigos de exceção
<a name="dicomweb-oidc-exceptions"></a>

Em caso de falha na autenticação, HealthImaging retorna os seguintes códigos de resposta de erro HTTP e mensagens corporais:


| Condição | Resposta AHI | 
| --- | --- | 
| O Lambda Authorizer não existe ou é inválido | 424 Configuração incorreta do autorizador | 
| Autorizador encerrado devido a falha na execução | 424 Falha no autorizador | 
| Qualquer outro erro não mapeado do autorizador | 424 Falha no autorizador | 
| O autorizador retornou uma resposta inválida/mal formada | 424 Configuração incorreta do autorizador | 
| O autorizador foi executado por mais de 1s | Tempo limite do autorizador 408 | 
| O token está expirado ou é inválido | 403 Token inválido ou expirado | 
| O AHI não pode federar a função IAM retornada devido à configuração incorreta do autorizador | 424 Configuração incorreta do autorizador | 
| O autorizador retornou uma função vazia | 403 Acesso negado | 
| A função retornada não pode ser chamada (configuração incorreta de assume-role/trust) | 424 Configuração incorreta do autorizador | 
| A taxa de solicitação excede os limites do DICOMweb gateway | 4.29 Solicitações demais | 
| Armazenamento de dados, função de retorno ou autorizador entre regiões Account/Cross  | 424 Autorizador: Acesso entre regiões Account/Cross  | 

## Exemplo de implementação
<a name="dicomweb-oidc-implementation"></a>

Este exemplo em Python demonstra uma função autorizadora lambda que verifica os tokens de acesso do Cognito a partir de eventos HealthImaging e retorna um AWS ARN de função do IAM com os privilégios apropriados. DICOMWeb 

O autorizador Lambda implementa dois mecanismos de armazenamento em cache para reduzir as chamadas externas e a latência de resposta. O JWKS (JSON Web Key Set) é obtido uma vez a cada hora e armazenado na pasta temporária da função, permitindo que as invocações subsequentes da função o leiam localmente em vez de buscá-lo na rede pública. Você também notará que um objeto de dicionário token\$1cache é instanciado no contexto global dessa função Lambda. As variáveis globais são compartilhadas por todas as invocações que reutilizam o mesmo contexto aquecido do Lambda. Graças a isso, os tokens verificados com sucesso podem ser armazenados neste dicionário e pesquisados rapidamente durante a próxima execução dessa mesma função Lambda. O método de armazenamento em cache representa uma abordagem generalista que pode caber em tokens de acesso emitidos pela maioria dos provedores de identidade. [Para uma opção de armazenamento em cache específica do AWS Cognito, consulte a seção [Gerenciando grupos de usuários e a seção de armazenamento em](https://docs.aws.amazon.com/cognito/latest/developerguide/managing-users.html) cache da documentação [do Cognito](https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-tokens-caching-tokens.html).AWS](https://docs.aws.amazon.com/cognito/latest/developerguide/what-is-amazon-cognito.html)

```
import json
import os
import time
import logging
from jose import jwk, jwt
from jose.exceptions import ExpiredSignatureError, JWTClaimsError, JWTError
import requests
import tempfile

# Configure logging
logger = logging.getLogger()
log_level = os.environ.get('LOG_LEVEL', 'WARNING').upper()
logger.setLevel(getattr(logging, log_level, logging.WARNING))

# Global token cache with TTL
token_cache = {}

# JWKS cache file path
JWKS_CACHE_FILE = os.path.join(tempfile.gettempdir(), 'jwks.json')
JWKS_CACHE_TTL = 3600  # 1 hour

# Load environment variables once
USER_POOL_ID = os.environ['USER_POOL_ID']
CLIENT_ID = os.environ['CLIENT_ID']
ROLE_ARN = os.environ.get('AHIDICOMWEB_READONLY_ROLE_ARN', '')

def cleanup_expired_tokens():
    """Remove expired tokens from cache"""
    now = int(time.time())
    expired_keys = [token for token, data in token_cache.items() if now > data['cache_expiry']]
    for token in expired_keys:
        del token_cache[token]

def get_cached_jwks():
    """Get JWKS from cache file if valid, otherwise return None """
    try:
        if os.path.exists(JWKS_CACHE_FILE):
            # Check if cache file is still valid
            cache_age = time.time() - os.path.getmtime(JWKS_CACHE_FILE)
            if cache_age < JWKS_CACHE_TTL:
                with open(JWKS_CACHE_FILE, 'r') as f:
                    jwks = json.load(f)
                    logger.debug(f'Using cached JWKS (age: {int(cache_age)}s)')
                    return jwks
            else:
                logger.debug(f'JWKS cache expired (age: {int(cache_age)}s)')
    except Exception as e:
        logger.debug(f'Error reading JWKS cache: {e}')
    
    return None

def cache_jwks(jwks):
    """Cache JWKS to file"""
    try:
        with open(JWKS_CACHE_FILE, 'w') as f:
            json.dump(jwks, f)
        logger.debug('JWKS cached successfully')
    except Exception as e:
        logger.debug(f'Error caching JWKS: {e}')

def fetch_jwks(jwks_url):
    """Fetch JWKS from URL and cache it"""
    logger.debug('Fetching JWKS from URL')
    jwks = requests.get(jwks_url, timeout=10).json()
    # Convert to dict for faster lookups
    jwks['keys_by_kid'] = {key['kid']: key for key in jwks['keys']}
    cache_jwks(jwks)
    return jwks

def is_token_cached(token):
    if token not in token_cache:
        return None
    
    cached = token_cache[token]
    now = int(time.time())
    
    if now > cached['cache_expiry']:
        del token_cache[token]
        return None
    
    return cached

def cache_token(token, payload):
    now = int(time.time())
    token_exp = payload.get('exp')
    cache_expiry = min(now + 60, token_exp)  # 1 minute or token expiry, whichever is sooner
    
    token_cache[token] = {
        'payload': payload,
        'cache_expiry': cache_expiry,
        'role_arn': ROLE_ARN
    }

def handler(event, context):
    cleanup_expired_tokens() # start be removing expired tokens from the cache
    try:
        # Extract token from bearerToken or authorizationToken field
        token = event.get('bearerToken')
        if not token:
            raise Exception('No token provided')
        
        # Check cache first
        cached = is_token_cached(token)
        if cached:
            logger.debug('Token found in cache, skipping verification')
            return {
                'isTokenValid': True,
                'roleArn': cached['role_arn']
            }
        
        # Get Cognito configuration
        region = context.invoked_function_arn.split(':')[3]
        
        # Get JWKS (cached or fresh)
        jwks_url = f'https://cognito-idp.{region}.amazonaws.com/{USER_POOL_ID}/.well-known/jwks.json'
        jwks = get_cached_jwks()
        if not jwks:
            jwks = fetch_jwks(jwks_url)
        
        # Decode token header to get kid
        headers = jwt.get_unverified_headers(token)
        kid = headers['kid']
        
        # Find the correct key
        key = None
        for jwk_key in jwks['keys']:
            if jwk_key['kid'] == kid:
                key = jwk_key
                break
        
        if not key:
            # Key not found - try refreshing JWKS in case of key rotation
            logger.debug('Key not found in cached JWKS, fetching fresh JWKS')
            jwks = fetch_jwks(jwks_url)
            for jwk_key in jwks['keys']:
                if jwk_key['kid'] == kid:
                    key = jwk_key
                    break
        
        if not key:
            raise Exception('Public key not found')
        
        # Construct the public key
        public_key = jwk.construct(key)
        
        # Verify and decode the token (includes expiry validation)
        payload = jwt.decode(
            token,
            public_key,
            algorithms=['RS256'],
            audience=CLIENT_ID,
            issuer=f'https://cognito-idp.{region}.amazonaws.com/{USER_POOL_ID}'
        )
        
        logger.debug('Token validated successfully')
        logger.debug('User: %s', payload.get('username', 'unknown'))
        
        # Cache the validated token
        cache_token(token, payload)
        
        # Return authorization response
        return {
            'isTokenValid': True,
            'roleArn': ROLE_ARN
        }
        
    except ExpiredSignatureError:
        logger.debug('Token expired')
        return {
            'isTokenValid': False,
            'roleArn': ''
        }
    except JWTClaimsError:
        logger.debug('Invalid token claims')
        return {
            'isTokenValid': False,
            'roleArn': ''
        }
    except JWTError as e:
        logger.debug('JWT validation error: %s', e)
        return {
            'isTokenValid': False,
            'roleArn': ''
        }
    except Exception as e:
        logger.debug('Authorization failed: %s', e)
        return {
            'isTokenValid': False,
            'roleArn': ''
        }
```