Estou tentando usar keycloak no meu aplicativo FastAPI Meu código
from fastapi import Depends, HTTPException, status
from fastapi.security import OAuth2PasswordBearer
from jose import jwt, JWTError
from keycloak import KeycloakOpenID
import requests
import logging
import os
from .config import settings
# Configure logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# Keycloak configuration
KEYCLOAK_SERVER_URL = settings.KEYCLOAK_SERVER_URL
KEYCLOAK_REALM = settings.KEYCLOAK_REALM
KEYCLOAK_CLIENT_ID = settings.KEYCLOAK_CLIENT_ID
KEYCLOAK_CLIENT_SECRET = settings.KEYCLOAK_CLIENT_SECRET
ALGORITHM = "RS256"
TOKEN_URL = f"{KEYCLOAK_SERVER_URL}/realms/fastapi-realm/protocol/openid-connect/token"
# Initialize KeycloakOpenID
keycloak_openid = KeycloakOpenID(
server_url=f"{KEYCLOAK_SERVER_URL}",
client_id=KEYCLOAK_CLIENT_ID,
realm_name=KEYCLOAK_REALM,
client_secret_key=KEYCLOAK_CLIENT_SECRET,
verify=False
)
config_well_known = keycloak_openid.well_known()
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
def verify_token(token: str = Depends(oauth2_scheme)):
try:
decoded_token = keycloak_openid.decode_token(
token,validate=True,
)
username = decoded_token['preferred_username']
logger.info(f"Decoded token: {decoded_token}")
# Verify the issuer claim
issuer = decoded_token["iss"]
expected_issuer = f"{KEYCLOAK_SERVER_URL}/realms/{KEYCLOAK_REALM}"
I# Token example -- token: {'exp': 1731303036, 'iat': 1731267036, 'jti': 'f1b71d25-4de6-4c03-b5f5-d9726b39d51f', 'iss': 'https://feast-keycloak.pimc-st.innodev.local/realms/feast-realm', 'aud': 'account', 'sub': 'ac48f45e-f26b-4380-bde8-e752febb6d18', 'typ': 'Bearer', 'azp': 'feast-client-id', 'session_state': 'b36cd197-247d-447e-9f3d-6cf1fecae7d6', 'acr': '1', 'allowed-origins': ['https://feast-frontend.pimc-st.innodev.local', '/*', 'http://localhost:5173'], 'realm_access': {'roles': ['default-roles-feast-realm', 'offline_access', 'uma_authorization']}, 'resource_access': {'account': {'roles': ['manage-account', 'manage-account-links', 'view-profile']}}, 'scope': 'profile email', 'sid': 'b36cd197-247d-447e-9f3d-6cf1fecae7d6', 'email_verified': False, 'name': 'A B', 'preferred_username': 'my_username', 'given_name': 'A', 'family_name': 'B', 'email': '[email protected]'}
logger.info(f"XXX_ issuer={issuer}")
logger.info(f"XXX_ expected_issuer={expected_issuer}")
if issuer != expected_issuer:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid issuer")
logger.info(f"username: {username}")
return decoded_token
except Exception as e:
raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid token")
No meu main.py tenho o seguinte código
from fastapi import Depends, FastAPI, HTTPException, status, Security
from .keycloak import verify_token, oauth2_scheme, KEYCLOAK_CLIENT_ID, KEYCLOAK_CLIENT_SECRET, TOKEN_URL
app = FastAPI()
@app.post("/project", response_model=schemas.Project, tags=["project",])
def create_project(project: schemas.CreateProject, db: Session = Depends(get_db), payload: dict = Security(verify_token)):
...
@app.post("/login")
def get_token(body: schemas.Login):
data = {
"grant_type": "password", # TODO: clarify grant_type client_credentials (requires only client id and secret or password - requires password and login)
"client_id": KEYCLOAK_CLIENT_ID,
"client_secret": KEYCLOAK_CLIENT_SECRET,
"password": body.password,
"username": body.login
}
response = requests.post(TOKEN_URL, data=data, verify=False)
return JSONResponse(status_code=response.status_code, content=response.json())
Estou obtendo o token com a ajuda do método /login. Então estou aplicando o token assim:
Para solicitação /project
tenho um erro:
"Token inválido"
Como corrigir o erro?
Estas 3 etapas podem verificar o token
Step 1
: Obtenha o certificado de chave pública do Keycloak para verificar a assinatura do token JWT.Step 2
: Converta o certificado em um formato de chave pública utilizável.Step 3
: Decodifique e verifique a assinatura do token JWT usando a chave pública, ignorando as verificações de público e emissor.Iniciando o Key Cloak pelo Docker Compose
Criar reino e usuário
realm
user
Instalar dependência no Python 3.12 com conda
Código do servidor FastAPI
api-server.py
Iniciando o servidor FastAPI
API document URL
Obter Token pelo Carteiro
URL
Input Body
Script
para atribuir variável user_tokenVerificar Token pelo Postman
URL
Input Body
Então o token do usuário foi verificado com valor OK (verdadeiro).
Resultado válido do token