PEP et PDP pour une autorisation sécurisée avec Cognito

Ce fichier a ete traduit automatiquement par IA, des erreurs peuvent survenir
Dans l'un de mes articles précédents Construire un BBQ connecté sans serveur en tant que SaaS - Partie 4 - AuthZ j'ai abordé le sujet de l'Authentication et de l'Authorization avec des PEP (Points de Mise en Application de Politique) distribués et un PDP (Point de Décision de Politique) centralisé. Dans cet article, je vais creuser un peu plus profondément et élargir cette configuration. J'explorerai comment ces concepts fonctionnent en pratique, les avantages qu'ils offrent, et comment nous pouvons les exploiter dans notre architecture sans serveur en utilisant AWS Lambda, API Gateway et Cognito User Pools.
De plus, je parlerai du modèle de contrôle d'accès basé sur les rôles (RBAC), comment l'implémenter à l'aide de groupes Cognito et de DynamoDB, et comment le cache peut booster les performances de notre système d'autorisation.
Toute la configuration, avec des instructions détaillées de déploiement, et tout le code peuvent être trouvés sur Serverless Handbook PEP et PDP
Commençons par un bref rappel.
Authentication (AuthN) vs. Authorization (AuthZ)
Il est crucial de faire la distinction entre Authentication et Authorization, deux termes qui sont souvent confondus, et que j'ai dû expliquer à de nombreuses reprises, mais qui servent des objectifs très différents.
Authentication (AuthN)
L'Authentication consiste à vérifier l'identité. Elle répond à la question, Qui êtes-vous ? Lorsqu'un utilisateur se connecte à notre application, l'authentication garantit qu'il est bien celui qu'il prétend être. Cela peut impliquer quelque chose d'aussi simple qu'un nom d'utilisateur et un mot de passe ou une authentication multifacteur (MFA) plus complexe.
Authorization (AuthZ)
Une fois l'identité de l'utilisateur authentifiée, l'authorization entre en jeu. Ce processus répond à la question, Que pouvez-vous faire ? L'authorization détermine quels ressources, données et actions un utilisateur est autorisé à accéder en fonction de ses rôles et permissions.
Que sont les PEP et PDP ?
Avant de plonger dans leur implémentation sur AWS, il est essentiel de comprendre les rôles que jouent les PEP et PDP dans l'autorisation.
PEP (Point de Mise en Application de Politique)
En termes simples, le PEP est le gardien. Ce sont les points de notre système où les décisions de contrôle d'accès sont appliquées. Lorsqu'un utilisateur tente d'accéder à une ressource protégée, le PEP est le composant chargé de vérifier si la requête est autorisée ou refusée en fonction des permissions de l'utilisateur.
Dans notre cas, l'Authorizer Lambda dans API Gateway agit comme le PEP. L'Authorizer Lambda intercepte chaque requête API entrante, valide le jeton JWT (généralement à partir de Cognito User Pool ou de n'importe quel fournisseur d'identité), et transmet les informations de l'utilisateur (claims) au PDP pour l'évaluation de l'autorisation.
Le PEP garantit que le JWT est valide, vérifie son expiration, vérifie sa signature, et valide les claims (comme aud, iss, et sub). Il transmet ensuite les claims au PDP pour une décision finale sur Whether l'utilisateur est autorisé à accéder à la ressource demandée.
PDP (Point de Décision de Politique)
Le PDP est l'endroit où réside la logique d'autorisation. Une fois que le PEP a vérifié le JWT et garantit que le jeton est valide, le PDP détermine si l'utilisateur est autorisé à accéder à la ressource demandée en fonction de ses rôles, permissions ou politiques.
Le PDP est un service micro indépendant, dans notre cas une fonction Lambda, qui effectue la décision d'autorisation réelle. Il vérifie les rôles de l'utilisateur, stockés dans la claim groups du JWT (de Cognito), et les compare aux permissions requises pour accéder à une ressource spécifique, stockées dans un stockage de données. Dans notre cas nous utiliserons DynamoDB.
Le PDP valide si l'utilisateur a les permissions nécessaires (comme Admin, User, ou Manager) pour accéder à la ressource (par exemple, GET /admin, POST /profile). Le PDP peut également intégrer une logique métier supplémentaire, comme la vérification de l'accès basé sur le temps ou le géofencing.
Avantages de l'utilisation de PEP et PDP dans l'Authorization
L'implémentation de PEP distribués et d'un PDP centralisé offre plusieurs avantages, en particulier à mesure que nos applications évoluent.
Séparation des responsabilités En séparant les responsabilités de mise en application (PEP) et de décision (PDP), nous obtenons un code plus propre et plus facile à maintenir. L'Authorizer Lambda (PEP) est uniquement axé sur la validation et l'application. Tandis que le PDP est dédié à l'évaluation des politiques.
Latence réduite : En plaçant les PEP près de l'endroit où les décisions doivent être appliquées, nous pouvons réduire la latence, avec une stratégie de cache cela peut être encore réduit.
Gestion : Avec un PDP centralisé, toute notre logique d'autorisation est centralisée en un seul endroit. Cela facilite la gestion et la mise à jour des politiques à mesure que nos exigences évoluent. Que ce soit pour modifier des rôles ou ajouter de nouveaux ensembles de permissions, avoir un PDP central réduit la charge de mise à jour des politiques à plusieurs endroits.
Cohérence et conformité : Chaque requête est évaluée par rapport au même ensemble de politiques, garantissant une prise de décision cohérente dans tout notre système.
Scalabilité : Les composants PEP et PDP scalent indépendamment en fonction de la demande. Si notre système doit gérer un plus grand volume de requêtes, API Gateway et Lambda peuvent s'adapter automatiquement. De plus, le PDP peut être optimisé pour les performances en implémentant un cache.
Flexibilité : Un PDP nous permet d'adapter le modèle d'autorisation à nos besoins. Si nos exigences changent (par exemple, passer à un contrôle d'accès basé sur les attributs (ABAC) ou introduire un système de permissions plus granulaire), nous pouvons facilement modifier le PDP pour accueillir ces changements sans affecter d'autres parties du système.
Utilisation de PEP et PDP dans AWS avec une architecture sans serveur
Sur AWS, l'intégration de PEP et PDP s'intègre parfaitement avec les composants sans serveur comme Lambda et API Gateway.
PEP - Lambda Authorizer d'API Gateway
Lorsqu'un client envoie une requête à notre point final API Gateway, le Lambda Authorizer (PEP) intercepte la requête avant qu'elle n'atteigne notre service backend. Notre implémentation effectuera plusieurs étapes clés.
Validation du JWT : Il décode le JWT, valide la signature, et vérifie si le jeton a expiré.
Transmission des claims : Après avoir vérifié le jeton, le Lambda Authorizer transmet les claims (telles que sub, groups, et role) au PDP pour des vérifications d'autorisation supplémentaires. Dans notre solution nous transmettrons en fait le jeton JWT entier.
Pour réduire le nombre d'appels à notre PEP et également au PDP nous pouvons utiliser le cache d'autorisation qui existe dans API Gateway.
PDP - Fonction Lambda de logique d'autorisation
Le PDP est implémenté comme une fonction Lambda séparée, dans notre cas, et recevra le jeton JWT entier, ou les claims, pour effectuer la logique d'autorisation, qui inclura plusieurs étapes.
- Vérifier le rôle de l'utilisateur (en utilisant la claim
groupsde Cognito). - Interroger une table DynamoDB qui contient des mappages rôle-permission (par exemple, quels rôles ont accès à quelles extrémités API).
- Évalue si le rôle de l'utilisateur correspond aux permissions requises pour la ressource ou l'extrémité API demandée.
ID Token vs Access Token
Alors que nous implémentons le flux de travail PEP et PDP, il est essentiel de comprendre la différence entre les ID Tokens et les Access Tokens, car les deux sont souvent utilisés dans les flux d'autorisation.
ID Token
Le ID Token est principalement utilisé pour l'authentication et contient des informations sur l'identité de l'utilisateur. Il contient des claims sur l'identité de l'utilisateur authentifié, comme le nom, l'email, et le téléphone.
Access Token
Le Access Token est utilisé pour accorder à l'utilisateur l'accès aux ressources protégées, l'autorisation. Le Access Token contient des informations sur les permissions de l'utilisateur, comme quelles ressources il est autorisé à accéder et les scopes qui lui ont été accordés, qui définissent ce que l'utilisateur peut faire (par exemple, read:profile, write:profile). Le jeton d'accès n'inclut pas la claim aud.
Personnalisation des jetons dans Cognito
Avec le déclencheur Lambda de génération de jetons Pre, nous ne pouvions auparavant personnaliser que le ID Token, donc il était souvent utilisé pour l'autorisation également. Avec l'introduction du nouvel événement V2 dans Cognito User Pools nous pouvons personnaliser à la fois le ID et le Access token.
Implémentation de PEP et PDP
Avec cette introduction terminée, plongeons dans l'implémentation d'un PEP et PDP avec RBAC. Notre PEP sera l'Authorizer Lambda dans API Gateway et notre PDP sera une fonction Lambda séparée. Le PDP utilisera les groupes Cognito et DynamoDB pour la logique d'autorisation RBAC.
Aperçu de l'architecture
Juste comme rappel, tout le code et l'architecture peuvent être trouvés sur Serverless Handbook PEP et PDP
Dans cette solution nous allons implémenter notre PEP en utilisant Lambda Authorizer dans API Gateway. Le PDP dans ce cas sera également implémenté à l'aide d'une fonction Lambda. Nous assignerons aux utilisateurs un Rôle en utilisant Cognito Groups et nous garderons une correspondance Rôle - Permission dans DynamoDB.

Pour mieux comprendre le flux lors d'un accès API.

Comme on le voit nous n'utiliserons pas un API Gateway pour notre PDP. À la place notre PEP appellera la fonction Lambda PDP. Il y a bien sûr des avantages et des inconvénients à cette approche. Côté avantages nous avons une latence plus faible, un appel Lambda direct est souvent plus rapide qu'un appel API. Une moindre coût car nous n'avons pas à payer pour l'appel API Gateway. Côté inconvénients, nous créons un couplage plus étroit et changer l'implémentation du PDP pourrait être plus difficile. Nous aurions besoin d'implémenter un cache séparé dans le PDP, en utilisant un API Gateway nous pourrions nous reposer sur le cache d'API Gateway.
Cependant, l'approche que vous choisissez doit être une approche cas par cas, il n'y a pas de règle d'or pour exactement comment implémenter cela.
Déploiement de l'authentication et de Cognito
La première chose que nous ferons est de déployer et de configurer Cognito et les ressources nécessaires pour la connexion. Nous configurerons le UserPool Cognito, la connexion gérée, et un simple site web qui gérera les callbacks de Cognito et affichera nos jetons JWT. Pour simplifier ce sera juste une page html statique depuis CloudFront et quelques fonctions Lambda@Edge. J'utiliserai la configuration que j'ai décrite dans ce blog post, donc pour une plongée profonde je vous recommande de le lire.
Donc comme première étape déployez Lambda@Edge, la distribution CloudFront, et le certificat SSL depuis Serverless Handbook PEP et PDP
Ensuite, déployons et configurons Cognito. Nous créerons le UserPool, un client, le style de connexion, etc.
AWSTemplateFormatVersion: "2010-09-09"
Transform: "AWS::Serverless-2016-10-31"
Description: Crée le User Pool et le Client utilisés pour l'Authentication
Parameters:
ApplicationName:
Type: String
Description: L'application qui possède cette configuration.
DomainName:
Type: String
Description: Le nom de domaine à utiliser pour cloudfront
HostedAuthDomainPrefix:
Type: String
Description: Le préfixe de domaine à utiliser pour l'interface utilisateur hébergée UserPool <HostedAuthDomainPrefix>.auth.[region].amazoncognito.com
Resources:
UserPool:
Type: AWS::Cognito::UserPool
Properties:
UsernameConfiguration:
CaseSensitive: false
AutoVerifiedAttributes:
- email
UserPoolName: !Sub ${ApplicationName}-user-pool
Schema:
- Name: email
AttributeDataType: String
Mutable: false
Required: true
- Name: name
AttributeDataType: String
Mutable: true
Required: true
UserPoolClient:
Type: AWS::Cognito::UserPoolClient
Properties:
UserPoolId: !Ref UserPool
GenerateSecret: True
AllowedOAuthFlowsUserPoolClient: true
CallbackURLs:
- !Sub https://${DomainName}/signin
AllowedOAuthFlows:
- code
- implicit
AllowedOAuthScopes:
- phone
- email
- openid
- profile
SupportedIdentityProviders:
- COGNITO
HostedUserPoolDomain:
Type: AWS::Cognito::UserPoolDomain
Properties:
Domain: !Ref HostedAuthDomainPrefix
ManagedLoginVersion: 2
UserPoolId: !Ref UserPool
ManagedLoginStyle:
Type: AWS::Cognito::ManagedLoginBranding
Properties:
ClientId: !Ref UserPoolClient
UserPoolId: !Ref UserPool
UseCognitoProvidedValues: true
UserPoolIdParameter:
Type: AWS::SSM::Parameter
Properties:
Name: !Sub /${ApplicationName}/userPoolId
Type: String
Value: !Ref UserPool
Description: Paramètre SSM pour l'ID du User Pool
Tags:
ApplicationName: !Ref ApplicationName
UserPoolHostedUiParameter:
Type: AWS::SSM::Parameter
Properties:
Name: !Sub /${ApplicationName}/userPoolHostedUi
Type: String
Value: !Sub https://${HostedAuthDomainPrefix}.auth.${AWS::Region}.amazoncognito.com/login?client_id=${UserPoolClient}&response_type=code&scope=email+openid+phone+profile&redirect_uri=https://${DomainName}/signin
Description: Paramètre SSM pour l'Interface Utilisateur Hébergée du User Pool
Tags:
ApplicationName: !Ref ApplicationName
Outputs:
CognitoUserPoolJwksUri:
Value: !Sub https://cognito-idp.${AWS::Region}.amazonaws.com/${UserPool}/.well-known/jwks.json
Description: L'URI jwks du UserPool
Export:
Name: !Sub ${AWS::StackName}:jwks-url
CognitoUserPoolID:
Value: !Ref UserPool
Description: L'ID du UserPool
CognitoAppClientID:
Value: !Ref UserPoolClient
Description: Le client de l'application
Export:
Name: !Sub ${AWS::StackName}:app-audience
CognitoUrl:
Description: L'URL
Value: !GetAtt UserPool.ProviderURL
CognitoHostedUI:
Value: !Sub https://${HostedAuthDomainPrefix}.auth.${AWS::Region}.amazoncognito.com/login?client_id=${UserPoolClient}&response_type=code&scope=email+openid+phone+profile&redirect_uri=https://${DomainName}/signin
Description: L'URL de l'interface utilisateur hébergée
Avec ce déploiement effectué nous pouvons passer à la Console et créer des groupes auxquels les utilisateurs peuvent être ajoutés. Je vais créer trois groupes, Admin, Developer, et Test. Cliquez sur Créer un groupe et donnez-lui un nom. Le nom du groupe représentera le Rôle que l'utilisateur aura et déterminera quelles permissions il/elle obtiendra, plus d'informations sur cette configuration plus loin.

Nous pouvons ensuite créer quelques utilisateurs et les assigner à l'un des groupes.
Pour tester cette configuration nous pouvons naviguer vers la page web déployée avec la distribution CloudFront et inspecter les jetons JWT, les cookies.

Si nous copions le jeton d'accès et le décodons, j'utilise jwt.io, nous pouvons voir que mon utilisateur a la claim cognito:groups que notre PEP et PDP utiliseront plus tard pour les permissions.
Configuration et déploiement du PDP
Ensuite nous pouvons déployer notre service d'Authorization, notre PDP, responsable de prendre les décisions de permission.
La logique sera implémentée dans une fonction Lambda et pour gérer les permissions basées sur les rôles, nous créerons une table DynamoDB qui stockera les permissions pour chaque rôle. Chaque permission définit quelles ressources l'utilisateur peut accéder, cela pourrait être une extrémité API spécifique et la méthode HTTP, mais bien sûr pas limité à cela. Nous modéliserons les données de la table
PK (Clé de partition): Le Rôle (par exemple, Admin, User). SK (Clé de tri): La ressource, par exemple l'extrémité et la Méthode par exemple GET /unicorn. Action: L'action par exemple GET, PUT, WRITE, READ, LIST etc Resource: La Ressource, par exemple l'extrémité /unicorn Effect: L'Effet, Allow ou Deny Description: Une description de la permission.
| PK | SK | Action | Resource | Effect | Description |
|---|---|---|---|---|---|
| Admin | GET /unicorn | GET | /unicorn | Allow | Admin peut accéder de tous les unicorns |
| Test | POST /unicorn | POST | /unicorn | Allow | Test peut poster sur les unicorns |
| Developer | DELETE /unicorn | DELETE | /unicorn | Deny | Manager ne peut pas supprimer un unicorn |
Cela nous permet de rechercher efficacement les permissions pour chaque rôle à l'aide d'une simple requête DynamoDB.
AWSTemplateFormatVersion: "2010-09-09"
Transform: "AWS::Serverless-2016-10-31"
Description: Application de service de locataire Connected BBQ
Parameters:
ApplicationName:
Type: String
Description: Nom de l'application propriétaire
UserManagementStackName:
Type: String
Description: Le nom de la pile qui contient la partie de gestion des utilisateurs, par exemple le UserPool Cognito
Globals:
Function:
Timeout: 30
MemorySize: 2048
Architectures:
- arm64
Runtime: python3.12
Resources:
PermissionsTable:
Type: AWS::DynamoDB::Table
Properties:
TableName:
Fn::Sub: ${ApplicationName}-pdp-role-permission-map
BillingMode: PAY_PER_REQUEST
AttributeDefinitions:
- AttributeName: PK
AttributeType: S
- AttributeName: SK
AttributeType: S
KeySchema:
- AttributeName: PK
KeyType: HASH
- AttributeName: SK
KeyType: RANGE
LambdaPDPFunction:
Type: AWS::Serverless::Function
Properties:
CodeUri: Lambda/AuthZ
Handler: authz.handler
Policies:
- DynamoDBReadPolicy:
TableName: !Ref PermissionsTable
Environment:
Variables:
JWKS_URL:
Fn::ImportValue: !Sub ${UserManagementStackName}:jwks-url
AUDIENCE:
Fn::ImportValue: !Sub ${UserManagementStackName}:app-audience
PERMISSIONS_TABLE:
!Ref PermissionsTable
Outputs:
PDPLambdaArn:
Value: !GetAtt LambdaPDPFunction.Arn
Description: L'ARN de la fonction Lambda PDP
Export:
Name: !Sub ${AWS::StackName}:pdp-lambda-arn
PDPLambdaName:
Value: !Ref LambdaPDPFunction
Description: Le nom de la fonction Lambda PDP
Export:
Name: !Sub ${AWS::StackName}:pdp-lambda-nameLogique d'autorisation des rôles
Le Lambda PDP décodera le JWT, récupérera le rôle à partir de la claim cognito:groups, et interrogera la table DynamoDB pour vérifier si le rôle a la permission d'accéder à la ressource demandée.
import os
import json
import jwt
import boto3
from jwt import PyJWKClient
from botocore.exceptions import ClientError
dynamodb = boto3.resource("dynamodb")
table = dynamodb.Table(os.environ["PERMISSIONS_TABLE"])
JWKS_URL = os.environ["JWKS_URL"]
AUDIENCE = os.environ["AUDIENCE"]
def handler(event, context):
data = event
jwt_token = data["jwt_token"]
resource = data["resource"]
action = data["action"]
return check_authorization(jwt_token, action, resource)
def check_authorization(jwt_token, action, resource):
try:
jwks_client = PyJWKClient(JWKS_URL)
signing_key = jwks_client.get_signing_key_from_jwt(jwt_token)
decoded_token = jwt.decode(
jwt_token,
signing_key.key,
algorithms=["RS256"],
audience=AUDIENCE,
)
role = (
decoded_token["cognito:groups"][0]
if "cognito:groups" in decoded_token
else None
)
if not role:
raise Exception("Unauthorized: Role not found in the token")
if validate_permission(role, action, resource):
response_body = generate_access(
decoded_token["sub"], "Allow", action, resource
)
return {
"statusCode": 200,
"body": json.dumps(response_body),
"headers": {"Content-Type": "application/json"},
}
except Exception as e:
print(f"Authorization error: {str(e)}")
response_body = generate_access(decoded_token["sub"], "Deny", action, resource)
return {
"statusCode": 403,
"body": json.dumps(response_body),
"headers": {"Content-Type": "application/json"},
}
def validate_permission(role, action, resource):
print(f"validate_permission Role: {role}, Action: {action}, Resource: {resource}")
try:
response = table.query(
KeyConditionExpression="PK = :role AND SK = :endpoint",
ExpressionAttributeValues={
":role": role,
":endpoint": f"{action} {resource}",
},
)
if response["Items"] and response["Items"][0]["Effect"] == "Allow":
return True
else:
return False
except ClientError as e:
print(f"Error querying DynamoDB: {e}")
return False
def generate_access(principal, effect, action, resource):
auth_response = {
"principalId": principal,
"effect": effect,
"action": action,
"resource": resource,
}
return auth_response
Déploiement de l'API et du PEP
Maintenant nous pouvons déployer notre API et notre PEP, Lambda Authorizer.
AWSTemplateFormatVersion: "2010-09-09"
Transform: "AWS::Serverless-2016-10-31"
Description: Crée l'API pour la gestion des certificats en auto-service
Parameters:
ApplicationName:
Type: String
Description: Nom de l'application propriétaire
UserManagementStackName:
Type: String
Description: Le nom de la pile qui contient la partie de gestion des utilisateurs, par exemple le UserPool Cognito
PDPStackName:
Type: String
Description: Le nom de la pile qui contient le service PDP
Globals:
Function:
Timeout: 30
MemorySize: 2048
Runtime: python3.12
Resources:
LambdaGetUnicorn:
Type: AWS::Serverless::Function
Properties:
CodeUri: Lambda/API/GetUnicorn
Handler: handler.handler
Events:
GetUnicorns:
Type: Api
Properties:
Path: /unicorn
Method: get
RestApiId: !Ref UnicornApi
UnicornApi:
Type: AWS::Serverless::Api
Properties:
Description: API pour créer et gérer les Unicorns
Name: !Sub ${ApplicationName}-api
StageName: prod
OpenApiVersion: '3.0.1'
AlwaysDeploy: true
EndpointConfiguration: REGIONAL
Cors:
AllowMethods: "'GET,PUT,POST,DELETE,OPTIONS'"
AllowHeaders: "'Content-Type,Authorization,X-Amz-Date,X-Api-Key,X-Amz-Security-Token'"
AllowOrigin: "'*'"
Auth:
AddDefaultAuthorizerToCorsPreflight: false
Authorizers:
LambdaRequestAuthorizer:
FunctionArn: !GetAtt LambdaApiAuthorizer.Arn
FunctionPayloadType: REQUEST
Identity:
Headers:
- Authorization
ReauthorizeEvery: 600
DefaultAuthorizer: LambdaRequestAuthorizer
LambdaApiAuthorizer:
Type: AWS::Serverless::Function
Properties:
CodeUri: Lambda/Authorizer/
Handler: auth.handler
Policies:
- LambdaInvokePolicy:
FunctionName:
Fn::ImportValue: !Sub ${PDPStackName}:pdp-lambda-name
Environment:
Variables:
JWKS_URL:
Fn::ImportValue: !Sub ${UserManagementStackName}:jwks-url
AUDIENCE:
Fn::ImportValue: !Sub ${UserManagementStackName}:app-audience
PDP_AUTHZ_ENDPOINT:
Fn::ImportValue: !Sub ${PDPStackName}:pdp-lambda-name
Nous définissons notre PEP comme l'authorizer par défaut de cette façon il sera ajouté à chaque ressource et méthode. Pour réduire le nombre d'appels à notre PDP le cache d'autorisation dans API Gateway est utilisé avec un TTL de 600 secondes.
Logique d'autorisation PEP
L'Authorizer Lambda PEP décodera le JWT, vérificera sa validité, puis appellera le PDP pour une décision finale de permission.
import os
import json
import jwt
import boto3
from jwt import PyJWKClient
lambda_client = boto3.client("lambda")
def handler(event, context):
print(f"Event: {json.dumps(event)}")
token = event["headers"].get("authorization", "")
path = event["path"]
method = event["httpMethod"]
if not token:
raise Exception("Unauthorized")
token = token.replace("Bearer ", "")
decoded_token = None
try:
jwks_url = os.environ["JWKS_URL"]
jwks_client = PyJWKClient(jwks_url)
signing_key = jwks_client.get_signing_key_from_jwt(token)
decoded_token = jwt.decode(
token,
signing_key.key,
algorithms=["RS256"],
audience=os.environ["AUDIENCE"],
)
data = {
"jwt_token": token,
"resource": path,
"action": method,
}
response = lambda_client.invoke(
FunctionName=os.environ["PDP_AUTHZ_ENDPOINT"],
InvocationType="RequestResponse",
Payload=json.dumps(data),
)
response_payload = json.loads(response["Payload"].read())
body = json.loads(response_payload["body"])
effect = body["effect"]
return generate_policy(
decoded_token["sub"], effect, event["methodArn"], decoded_token
)
except Exception as e:
print(f"Authorization error: {str(e)}")
return generate_policy(
decoded_token["sub"], "Deny", event["methodArn"], decoded_token
)
def generate_policy(principal_id, effect, resource):
auth_response = {
"principalId": principal_id,
"policyDocument": {
"Version": "2012-10-17",
"Statement": [
{"Action": "execute-api:Invoke", "Effect": effect, "Resource": resource}
],
},
}
return auth_response
Importance du cache
Le cache est important pour optimiser notre flux d'autorisation. En réduisant les appels au PDP et en accélérant la prise de décision, le cache améliore les performances globales, la scalabilité et l'efficacité énergétique de notre application.
Réduire la latence : En mettant en cache les données de rôle et de permission, le PEP évite des appels répétés à notre PDP, ce qui entraîne des temps de réponse plus rapides et une latence plus faible pour chaque requête. Diminuer la charge du PDP : Le cache minimise le nombre d'appels effectués à notre PDP, réduisant le risque d'atteindre des limites de débit ou de throttling. Améliorer la scalabilité : Avec moins de requêtes frappant notre PDP, notre architecture peut s'adapter plus efficacement. Réduire les coûts : Le cache réduit le besoin d'invocations PDP répétées, ce qui réduit directement les coûts d'invocation Lambda.
Résumé et conclusion
L'implémentation de PEP et PDP dans notre flux d'autorisation offre une manière hautement scalable, flexible et sécurisée de contrôler l'accès aux ressources. En utilisant AWS Lambda et API Gateway, nous pouvons construire un système d'autorisation sans serveur qui sépare les préoccupations d'authentication et d'autorisation, s'adapte à la demande et simplifie la gestion des politiques.
Avec l'ajout du contrôle d'accès basé sur les rôles et DynamoDB pour stocker les permissions, combiné à un cache en mémoire pour des performances améliorées, nous pouvons créer une solution d'autorisation qui répond aux besoins actuels et futurs.
Comprendre la différence entre les ID Tokens et les Access Tokens garantit que notre système utilise chacun de manière appropriée, nous aidant à construire un système d'autorisation plus sécurisé et efficace.
Bonne programmation, et restez sécurisés !
Code source
Toute la configuration, avec des instructions détaillées de déploiement, et tout le code peuvent être trouvés sur Serverless Handbook PEP et PDP
Derniers mots
N'oubliez pas de me suivre sur LinkedIn et X pour plus de contenu, et lisez le reste de mes Blogs
Comme le dit Werner ! Maintenant allez construire !