Pular para o conteúdo principal

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:

  1. Acesse o menu GK Auth Bastion → Settings → Auth Groups.
  2. Edite o Auth Group desejado.
  3. 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çalho X-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"}