Access Management
How to follow this chapter
Inputs: clientEmail, clientPassword and the delivered verification code.
Steps and expected result: First factor requests verification; second factor returns the client session. Store both tokens after login or refresh. Logout ends the current session.
Alternative or later actions: Use the login recipe when starting a session; refresh and logout are later session-management actions.
DTO source: AccessTokenOutputModel, AuthenticationSecondFactorOutputModel
Variables assigned in an earlier block are reused by later blocks. Client-entered values come from your UI; keep identifiers and lookup results in the current client context. Do not create a fresh context between steps.
Two-factor authentication
Action: Complete the two authentication factors.
Keep / expected result: Store userId, accessToken, refreshToken and validUntil from the final response.
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", guestToken);
// First factor authentication
var payload = JsonConvert.SerializeObject(new { AccountNumber = clientEmail, Password = clientPassword });
(await httpClient.PostAsync("api/v1/users/authentication/first-factor", new StringContent(payload, Encoding.UTF8, "application/json"))).EnsureSuccessStatusCode();
// Collect authenticationCode from the configured second-factor channel.
// Second factor authentication
payload = JsonConvert.SerializeObject(new { AccountNumber = clientEmail, Token = authenticationCode });
var httpResponseMessage = await httpClient.PostAsync("api/v1/users/authentication/second-factor", new StringContent(payload, Encoding.UTF8, "application/json"));
httpResponseMessage.EnsureSuccessStatusCode();
var resultAsString = await httpResponseMessage.Content.ReadAsStringAsync();
var authenticationResult = JsonConvert.DeserializeObject<AuthenticationSecondFactorOutputModel>(resultAsString);
// Now, set the CLIENT token for the next requests
var userId = authenticationResult.Id;
var accessToken = authenticationResult.AccessToken;
var refreshToken = authenticationResult.RefreshToken;
var validUntil = authenticationResult.ValidUntil;
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
// First factor authentication
JsonObject firstFactorPayload = new JsonObject();
firstFactorPayload.addProperty("AccountNumber", clientEmail);
firstFactorPayload.addProperty("Password", clientPassword);
HttpRequest firstFactorRequest = HttpRequest.newBuilder()
.timeout(Duration.ofMinutes(1))
.uri(Settings.wmsUri("users/authentication/first-factor"))
.header("Accept-Language", "de-DE")
.header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(firstFactorPayload.toString()))
.build();
var firstFactorResponse = httpClient.send(firstFactorRequest, BodyHandlers.ofString());
Api.requireSuccess(firstFactorResponse);
// Collect authenticationCode from the configured second-factor channel.
// Second factor authentication
JsonObject secondFactorPayload = new JsonObject();
secondFactorPayload.addProperty("AccountNumber", clientEmail);
secondFactorPayload.addProperty("Token", authenticationCode);
HttpRequest secondFactorRequest = HttpRequest.newBuilder()
.timeout(Duration.ofMinutes(1))
.uri(Settings.wmsUri("users/authentication/second-factor"))
.header("Accept-Language", "de-DE")
.header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(secondFactorPayload.toString()))
.build();
var response = httpClient.send(secondFactorRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
AuthenticationSecondFactorOutputModel authenticationResult = new Gson().fromJson(response.body(), AuthenticationSecondFactorOutputModel.class);
// Now, set the CLIENT token for the next requests
var userId = authenticationResult.Id;
var accessToken = authenticationResult.AccessToken;
var refreshToken = authenticationResult.RefreshToken;
var validUntil = authenticationResult.ValidUntil;
Logout
Action: End the current session.
Keep / expected result: Clear the stored session after successful logout.
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
(await httpClient.PostAsync("api/v1/users/authentication/logout", null)).EnsureSuccessStatusCode();
HttpRequest theRequest = HttpRequest.newBuilder()
.timeout(Duration.ofMinutes(1))
.uri(Settings.wmsUri("users/authentication/logout"))
.header("Accept-Language", "de-DE")
.header("Authorization", "Bearer " + accessToken)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.noBody())
.build();
var response = httpClient.send(theRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
Refresh authentication token
Action: Request a refreshed session.
Keep / expected result: Replace both stored tokens and validUntil with the returned values.
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", guestToken);
payload = JsonConvert.SerializeObject(new
{
UserId = userId,
RefreshToken = refreshToken,
AccessToken = accessToken
});
httpResponseMessage = await httpClient.PostAsync("api/v1/users/authentication/access-token/refresh", new StringContent(payload, Encoding.UTF8, "application/json"));
httpResponseMessage.EnsureSuccessStatusCode();
resultAsString = await httpResponseMessage.Content.ReadAsStringAsync();
var accessTokenOutputModel = JsonConvert.DeserializeObject<AccessTokenOutputModel>(resultAsString);
// Set new CLIENT token for the next HTTP requests
accessToken = accessTokenOutputModel.AccessToken;
refreshToken = accessTokenOutputModel.RefreshToken;
validUntil = accessTokenOutputModel.ValidUntil;
httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
JsonObject thePayload = new JsonObject();
thePayload.addProperty("UserId", userId);
thePayload.addProperty("RefreshToken", refreshToken);
thePayload.addProperty("AccessToken", accessToken);
HttpRequest theRequest = HttpRequest.newBuilder()
.timeout(Duration.ofMinutes(1))
.uri(Settings.wmsUri("users/authentication/access-token/refresh"))
.header("Accept-Language", "de-DE")
.header("Authorization", "Bearer " + Settings.GUEST_TOKEN)
.header("Content-Type", "application/json")
.POST(HttpRequest.BodyPublishers.ofString(thePayload.toString()))
.build();
var response = httpClient.send(theRequest, BodyHandlers.ofString());
Api.requireSuccess(response);
AccessTokenOutputModel accessTokenOutputModel = new Gson().fromJson(response.body(), AccessTokenOutputModel.class);
// New CLIENT token for the next HTTP requests
accessToken = accessTokenOutputModel.AccessToken;
refreshToken = accessTokenOutputModel.RefreshToken;
validUntil = accessTokenOutputModel.ValidUntil;
Recognize the result
After second-factor authentication or refresh: replace the stored token pair. This is a projection of the response.
{"id":123,"accessToken":"<access-token>","refreshToken":"<refresh-token>","validUntil":"2026-10-13T12:00:00Z"}The DTO catalog contains the remaining response properties. A failed request must be handled before advancing to the next step; see Error handling.