Resumo
O GuardianKey Auth Bastion (GKAB) requer um módulo integrador para gerenciar a criação e encerramento de sessões no sistema protegido.
Esta documentação detalha o processo de integração.
O GKAB interage com sistemas OAuth2/OIDC e, após autenticar um usuário, envia uma requisição POST contendo informações necessárias para criar uma sessão no sistema protegido. O endpoint do módulo integrador deve retornar os dados do token que será inserido no navegador do usuário, bem como a URL para redirecionamento após a autenticação.
O módulo integrador deve interagir com o sistema protegido para criar a sessão, o que pode ser feito usando sua API ou mesmo acesso direto ao banco ou armazenamento de sessões.
Requisitos para Integração
Para realizar a integração, o módulo integrador deve atender aos seguintes requisitos:
- Hospedagem: Deve estar hospedado em uma URL acessível pelo GuardianKey Controller (exemplo:
http://meuservidor/qualquer_path
). Essa URL deve ser configurada no GKAB. - Validação de Requisições: O endpoint deve validar as requisições utilizando a chave enviada no cabeçalho (header)
X-API-Key
. - Funções Disponíveis: O endpoint deve implementar duas funções principais:
- Login: Responsável por criar a sessão do usuário no sistema protegido.
- Logout: Responsável por encerrar a sessão do usuário no sistema protegido.
A seguir, são descritas as entradas e saídas esperadas para cada uma dessas funções.
Autenticação das Requisições
Todas as requisições enviadas pelo GuardianKey Controller incluem o cabeçalho X-API-Key
, que contém o hash SHA256 da API Key configurada no Auth Group correspondente.
Para localizar a API Key associada ao seu Auth Group, siga os passos abaixo:
- Acesse o menu GK Auth Bastion → Settings → Auth Groups.
- Edite o Auth Group desejado.
- Localize o campo API Key (readonly) e copie o valor exibido.
O módulo integrador deve calcular o hash SHA256 dessa API Key para utilizá-lo como valor do cabeçalho X-API-Key
. Veja o exemplo abaixo:
import hashlib
minha_api_key = "sua_api_key_aqui"
xapikey = hashlib.sha256(minha_api_key.encode()).hexdigest()
Certifique-se de que o valor gerado seja utilizado para validar as requisições recebidas pelo módulo integrador.
Processo de Login
Durante a etapa de login, o GuardianKey Controller envia uma requisição ao módulo integrador contendo os dados do usuário autenticado. O módulo integrador deve processar essas informações, que incluem a chave "action": "login"
e os dados fornecidos pelo sistema OAuth2/OIDC configurado, para criar uma sessão no sistema protegido. Esse processo garante que o usuário tenha acesso ao ambiente desejado de forma segura, monitorada e controlada.
Exemplo de Requisição
Abaixo está um exemplo de payload enviado pelo GuardianKey Controller ao módulo integrador para a criação da sessão na aplicação protegida:
{
"action": "login",
"sub": "19dfab21-6eaa-4db5-bae6-c69225c2b22d",
"email_verified": false,
"name": "João Silva",
"preferred_username": "11111111111",
"given_name": "João",
"family_name": "Silva",
"email": "[email protected]",
"id_token": "",
"oauth2_token": {
"access_token": "..-",
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": "",
"token_type": "Bearer",
"id_token": "",
"not-before-policy": 0,
"session_state": "9f14ea07-ab00-40c4-99b7-2ab09b890f99",
"scope": "openid profile email",
"expires_at": 1747186699
}
}
Resposta Esperada
O módulo integrador deve retornar os seguintes dados:
- Método de autenticação: Indica como o navegador do usuário será autenticado (ex.: via cookies).
- Nomes e valores dos cookies: Informações necessárias para autenticar a sessão do usuário.
- URL de redirecionamento: Caminho para onde o navegador do usuário será redirecionado após o login.
- Caminho dos cookies: Define o escopo dos cookies no navegador.
Exemplo de Resposta
{
"method": "cookie",
"cookie_name": ["cookie_session", "cookie_session2"],
"token": ["<token_gerado>", "<valor>"],
"next_url": "/home",
"cookie_path": "/"
}
Observações
- O campo
token
deve conter os valores dos cookies, na mesma ordem que estão em "cookie_name". - A URL de redirecionamento (
next_url
) deve apontar para a página inicial ou dashboard do sistema protegido.
Processo de Logout
Durante a etapa de logout, o GuardianKey Controller envia uma requisição ao módulo integrador contendo os dados do usuário autenticado. O módulo integrador deve processar essas informações, que incluem a chave "action": "logout"
e os dados fornecidos pelo sistema OAuth2/OIDC configurado, para encerrar a sessão no sistema protegido. Esse processo garante que o acesso do usuário ao ambiente seja revogado de forma segura e controlada.
Exemplo de Requisição
Abaixo está um exemplo de payload enviado pelo GuardianKey Controller ao módulo integrador para o encerramento da sessão na aplicação protegida:
{
"action": "logout",
"id_token": "xxx",
"sub": "19dfab21-6eaa-4db5-bae6-c69225c2b22d",
"preferred_username": "joao.silva",
"name": "João Silva",
"login_data": {
"method": "cookie",
"cookie_name": ["cookie_session", "cookie_session2"],
"token": ["<token_gerado>", "<valor>"],
"next_url": "/home",
"cookie_path": "/"
}
}
Resposta Esperada
O módulo integrador deve retornar os seguintes dados:
- Método de autenticação: Indica como o navegador do usuário será desautenticado (ex.: via cookies).
- Nomes e valores dos cookies: Informações necessárias para revogar a sessão do usuário.
- URL de redirecionamento: Caminho para onde o navegador do usuário será redirecionado após o logout.
Exemplo de Resposta
{
"method": "cookie",
"cookie_name": ["cookie_session", "cookie_session2"],
"next_url": "/login"
}
Observações
- O campo
cookie_name
deve conter os nomes dos cookies que precisam ser removidos ou invalidados. - A URL de redirecionamento (
next_url
) deve apontar para a página de login ou outra página apropriada após o logout.
Exceções
Durante a integração, podem ocorrer exceções que devem ser tratadas adequadamente pelo módulo integrador. Abaixo está um exemplo de resposta JSON que o módulo integrador pode retornar em caso de erro, como uma chave de API inválida:
{
"error": "Invalid API key. It should be the sha256 hash of the API key you found in the panel.",
"received": "valor_recebido_no_cabecalho_X-API-Key",
"next_url": "/login"
}
Observações
- O campo
error
deve descrever o motivo do erro de forma clara. - O campo
received
pode ser usado para depuração, indicando o valor recebido no cabeçalhoX-API-Key
. - O campo
next_url
deve apontar para a página de login ou outra página apropriada para redirecionamento após o erro.
Código de exemplo para o integrador
from datetime import datetime
import hashlib
import sqlite3
from fastapi import FastAPI, Request
from pydantic import BaseModel
import os
secret_key = "SW2YcwTIb9zpOOhoPsMm" # Replace with your actual secret key
xapikey = "xxxxxxx" # Replace with your actual API key
app = FastAPI()
@app.get("/")
async def read_root():
return {"message": "Bem-vindo à API FastAPI!"}
def get_user(username):
conn = sqlite3.connect("/grafana/grafana.db")
cursor = conn.cursor()
cursor.execute("SELECT id,login,email FROM user WHERE login = ?", (username,))
user = cursor.fetchone()
conn.close()
if user:
return {
"id": user[0],
"login": user[1],
"email": user[2]
}
else:
return None
def delete_session_in_db(userdb,post_data):
now = int(datetime.timestamp(datetime.now()))
conn = sqlite3.connect("/grafana/grafana.db")
cursor = conn.cursor()
hashedToken = None
try:
if "login_data" in post_data:
login_data = post_data["login_data"]
unhashedToken = login_data["token"][0] if "token" in login_data and len(login_data["token"]) > 0 else None
hashedToken = hashlib.sha256((unhashedToken + secret_key).encode()).hexdigest() if unhashedToken else None
except:
pass
if hashedToken:
cursor.execute(
"UPDATE user_auth_token SET revoked_at = ? WHERE auth_token = ?",
(now, hashedToken),
)
else:
cursor.execute(
"UPDATE user_auth_token SET revoked_at = ? WHERE user_id = ?",
(now, userdb["id"]),
)
conn.commit()
conn.close()
def create_session_in_db(userdb,post_data):
user_agent = post_data["user_agent"] if "user_agent" in post_data else ""
client_ip = post_data["client_ip"] if "client_ip" in post_data else ""
now = int(datetime.timestamp(datetime.now()))
conn = sqlite3.connect("/grafana/grafana.db")
cursor = conn.cursor()
unhashedToken = os.urandom(16).hex()
hashedToken = hashlib.sha256((unhashedToken + secret_key).encode()).hexdigest()
cursor.execute(
"INSERT INTO user_auth_token (user_id, auth_token, prev_auth_token, user_agent, client_ip, auth_token_seen, seen_at, rotated_at, created_at, updated_at) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)",
(userdb["id"], hashedToken, hashedToken, user_agent, client_ip, 0, now, now, now, now),
)
conn.commit()
conn.close()
return unhashedToken
@app.post("/create_session")
async def create_session(request: Request):
if request.headers["X-API-Key"] != xapikey:
return {
"error": "Invalid API key. It should be the sha256 hash of the API key you found the the panel.",
"received": request.headers["X-API-Key"],
"next_url": "/login",
}
post_data = await request.json()
if post_data["action"] == "login":
username = post_data['preferred_username'] if 'preferred_username' in post_data else None
userdb = get_user(username)
if userdb == None:
return {
"error": "Usuário não encontrado",
"received": post_data,
"next_url": "/login",
}
token = create_session_in_db(userdb,post_data)
timestamp_next_hour = int(datetime.timestamp(datetime.now())) + 3600
return {
"method": "cookie",
"cookie_name": ["grafana_session", "grafana_session_expiry"],
"token": [token, timestamp_next_hour],
"next_url": "/dashboards",
"cookie_path": "/",
}
elif post_data["action"] == "logout":
username = post_data['preferred_username'] if 'preferred_username' in post_data else None
userdb = get_user(username)
delete_session_in_db(userdb,post_data)
return {
"method": "cookie",
"cookie_name": ["grafana_session", "grafana_session_expiry"],
"next_url": "/login",
}
else:
return {"error": "Invalid action"}