Kodeeksempler¶
Eksemplerne dækker de samme 7 scenarier i begge sprog, så du kan læse Python og C# side om side.
| # | Scenarie | Endpoint / handling |
|---|---|---|
| 1 | Hent brugerprofil | GET /v1/user |
| 2 | Hent autorisationsliste | GET /v1/user/authorizations |
| 3 | Skift autorisation | GET /v1/user + X-VDX-AuthorizationId |
| 4 | Slip autorisation | GET /v1/user uden header |
| 5 | Hent gruppe + parent-træ | GET /v1/user/group, GET /v1/user/group/parents |
| 6 | Token-refresh | Keycloak token_endpoint |
| 7 | Fejlhåndtering | 401 / 403 fra Auth API |
Forudsætninger i eksemplerne¶
Erstat placeholder-værdier:
| Variabel | Beskrivelse |
|---|---|
ACCESS_TOKEN |
JWT access token fra Keycloak efter login |
AUTH_API_BASE |
https://mgmtauthapi.vconf-stage.dk (stage) |
KEYCLOAK_TOKEN_URL |
Fra OIDC discovery token_endpoint |
CLIENT_ID |
Din Keycloak-klient |
REFRESH_TOKEN |
Refresh token fra login |
Python kræver: pip install requests
1. Hent brugerprofil (GET /v1/user)¶
Kald straks efter login — uden succesfuldt svar har du ingen gyldig VDX-kontekst.
import requests
AUTH_API_BASE = "https://mgmtauthapi.vconf-stage.dk"
ACCESS_TOKEN = "DIT_ACCESS_TOKEN" # fra Keycloak efter login
def auth_headers(access_token: str, authorization_id: int | None = None) -> dict:
headers = {"Authorization": f"Bearer {access_token}"}
if authorization_id and authorization_id > 0:
headers["X-VDX-AuthorizationId"] = str(authorization_id)
return headers
response = requests.get(
f"{AUTH_API_BASE}/v1/user",
headers=auth_headers(ACCESS_TOKEN),
timeout=30,
)
if response.status_code == 200:
profile = response.json()
print(f"Velkommen {profile['firstname']} — gruppe {profile['organisationGroup']}")
elif response.status_code in (401, 403):
raise RuntimeError("Login afvist — profil-kald fejlede")
else:
response.raise_for_status()
using System.Net.Http.Headers;
using System.Text.Json;
const string AuthApiBase = "https://mgmtauthapi.vconf-stage.dk";
const string AccessToken = "DIT_ACCESS_TOKEN"; // fra Keycloak efter login
static HttpRequestMessage CreateAuthApiRequest(HttpMethod method, string path, int? authorizationId = null)
{
var request = new HttpRequestMessage(method, $"{AuthApiBase}{path}");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", AccessToken);
if (authorizationId is > 0)
request.Headers.TryAddWithoutValidation("X-VDX-AuthorizationId", authorizationId.Value.ToString());
return request;
}
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
using var request = CreateAuthApiRequest(HttpMethod.Get, "/v1/user");
using var response = await http.SendAsync(request);
if (response.StatusCode == System.Net.HttpStatusCode.OK)
{
var json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
var profile = doc.RootElement;
var groupId = profile.GetProperty("organisationGroup").GetInt64();
Console.WriteLine($"Gruppe: {groupId}");
}
else if (response.StatusCode is System.Net.HttpStatusCode.Unauthorized
or System.Net.HttpStatusCode.Forbidden)
{
throw new InvalidOperationException("Login afvist — profil-kald fejlede");
}
else
{
response.EnsureSuccessStatusCode();
}
2. Hent autorisationsliste (GET /v1/user/authorizations)¶
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
using var request = CreateAuthApiRequest(HttpMethod.Get, "/v1/user/authorizations");
using var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
foreach (var auth in doc.RootElement.EnumerateArray())
{
var id = auth.GetProperty("id").GetInt32();
var groupName = auth.GetProperty("groupName").GetString();
var role = auth.GetProperty("role").GetString();
Console.WriteLine($"{id}: {groupName} ({role})");
}
3. Skift autorisation¶
Verificér skiftet mod API'et før du persisterer valget i session/cookie.
AUTHORIZATION_ID = 42 # fra autorisationslisten
response = requests.get(
f"{AUTH_API_BASE}/v1/user",
headers=auth_headers(ACCESS_TOKEN, authorization_id=AUTHORIZATION_ID),
timeout=30,
)
if response.status_code == 403:
print("Autorisation afvist — behold eksisterende kontekst")
elif response.status_code == 200:
profile = response.json()
assert profile["isAuthorization"] is True
# Persister AUTHORIZATION_ID i session/cookie
print(f"Skiftet til {profile['organisationName']} (gruppe {profile['organisationGroup']})")
const int authorizationId = 42;
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
using var request = CreateAuthApiRequest(HttpMethod.Get, "/v1/user", authorizationId);
using var response = await http.SendAsync(request);
if (response.StatusCode == System.Net.HttpStatusCode.Forbidden)
{
Console.WriteLine("Autorisation afvist — behold eksisterende kontekst");
}
else if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
var isAuth = doc.RootElement.GetProperty("isAuthorization").GetBoolean();
// Persister authorizationId i session/cookie
Console.WriteLine($"Skiftet — isAuthorization: {isAuth}");
}
4. Slip autorisation¶
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
using var request = CreateAuthApiRequest(HttpMethod.Get, "/v1/user"); // ingen authorizationId
using var response = await http.SendAsync(request);
response.EnsureSuccessStatusCode();
var json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
var isAuth = doc.RootElement.GetProperty("isAuthorization").GetBoolean();
// Fjern persisteret autorisations-ID fra session/cookie
5. Hent gruppe + parent-træ¶
auth_id = 42 # eller None for basis-kontekst
# Flad gruppe
group_resp = requests.get(
f"{AUTH_API_BASE}/v1/user/group",
headers=auth_headers(ACCESS_TOKEN, authorization_id=auth_id),
timeout=30,
)
group_resp.raise_for_status()
group = group_resp.json()
# Parent-træ
tree_resp = requests.get(
f"{AUTH_API_BASE}/v1/user/group/parents",
headers=auth_headers(ACCESS_TOKEN, authorization_id=auth_id),
timeout=30,
)
tree_resp.raise_for_status()
tree = tree_resp.json()
int? authId = 42; // eller null for basis-kontekst
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
// Flad gruppe
using (var groupReq = CreateAuthApiRequest(HttpMethod.Get, "/v1/user/group", authId))
using (var groupResp = await http.SendAsync(groupReq))
{
groupResp.EnsureSuccessStatusCode();
var groupJson = await groupResp.Content.ReadAsStringAsync();
// deserialiser til Group DTO efter behov
}
// Parent-træ
using (var treeReq = CreateAuthApiRequest(HttpMethod.Get, "/v1/user/group/parents", authId))
using (var treeResp = await http.SendAsync(treeReq))
{
treeResp.EnsureSuccessStatusCode();
var treeJson = await treeResp.Content.ReadAsStringAsync();
}
6. Token-refresh (OIDC mod Keycloak)¶
KEYCLOAK_TOKEN_URL = "https://login.vconf-stage.dk/auth/realms/broker/protocol/openid-connect/token"
CLIENT_ID = "din-keycloak-klient-id"
REFRESH_TOKEN = "DIT_REFRESH_TOKEN"
response = requests.post(
KEYCLOAK_TOKEN_URL,
data={
"grant_type": "refresh_token",
"client_id": CLIENT_ID,
"refresh_token": REFRESH_TOKEN,
},
timeout=30,
)
if response.status_code == 200:
tokens = response.json()
new_access_token = tokens["access_token"]
new_refresh_token = tokens.get("refresh_token", REFRESH_TOKEN)
# Gem nye tokens i session/cookie
else:
# Refresh fejlede — log brugeren ud
raise RuntimeError("Session udløbet")
const string KeycloakTokenUrl =
"https://login.vconf-stage.dk/auth/realms/broker/protocol/openid-connect/token";
const string ClientId = "din-keycloak-klient-id";
const string RefreshToken = "DIT_REFRESH_TOKEN";
using var http = new HttpClient { Timeout = TimeSpan.FromSeconds(30) };
var form = new FormUrlEncodedContent(new Dictionary<string, string>
{
["grant_type"] = "refresh_token",
["client_id"] = ClientId,
["refresh_token"] = RefreshToken,
});
using var response = await http.PostAsync(KeycloakTokenUrl, form);
if (response.IsSuccessStatusCode)
{
var json = await response.Content.ReadAsStringAsync();
using var doc = JsonDocument.Parse(json);
var newAccessToken = doc.RootElement.GetProperty("access_token").GetString();
// Gem nye tokens — opdater også refresh_token hvis Keycloak roterer
}
else
{
throw new InvalidOperationException("Session udløbet — log brugeren ud");
}
7. Fejlhåndtering (401/403)¶
Implementér fail-closed når profil-kald fejler efter login.
def get_user_profile_or_fail(access_token: str) -> dict:
response = requests.get(
f"{AUTH_API_BASE}/v1/user",
headers=auth_headers(access_token),
timeout=30,
)
if response.status_code == 401:
# Forsøg token-refresh, ellers logud
raise PermissionError("Token ugyldigt eller udløbet")
if response.status_code == 403:
# Deaktiveret bruger eller ugyldig autorisation
raise PermissionError("Adgang nægtet — log brugeren ud")
if response.status_code >= 500:
raise ConnectionError("Auth API utilgængeligt")
response.raise_for_status()
return response.json()
async Task<JsonDocument> GetUserProfileOrFailAsync(HttpClient http, string accessToken)
{
using var request = new HttpRequestMessage(HttpMethod.Get, $"{AuthApiBase}/v1/user");
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
using var response = await http.SendAsync(request);
return response.StatusCode switch
{
System.Net.HttpStatusCode.OK =>
JsonDocument.Parse(await response.Content.ReadAsStringAsync()),
System.Net.HttpStatusCode.Unauthorized =>
throw new UnauthorizedAccessException("Token ugyldigt eller udløbet"),
System.Net.HttpStatusCode.Forbidden =>
throw new UnauthorizedAccessException("Adgang nægtet — log brugeren ud"),
>= System.Net.HttpStatusCode.InternalServerError =>
throw new HttpRequestException("Auth API utilgængeligt"),
_ => throw new HttpRequestException($"Uventet status: {response.StatusCode}"),
};
}
Se også¶
- Endpoints — request/response reference
- Modeller — felt-betydninger
- Login-flow — hvornår
/v1/userkaldes obligatorisk