Using the Auth API
Two POST requests are needed to authenticate a user:
- First call
/api/auth/startto start the authentication process - Then call
/api/auth/pollevery (pollingInterval* seconds) to check whether the user has authenticated
The /api/auth/start request returns a json object that needs to be used for the second post request to /api/auth/poll
Example request to authenticate a person
## Request for authentication via e-sim
POST /api/auth/start HTTP/1.1
Host: onboardingdev.taktikal.is
Accept: application/json
Content-Type: application/json
{
"PhoneNumber": "1234567",
"FlowKey": "abc123abc123",
"AuthenticationContextType": "Sim",
"IncludeVerificationCode": true
}
## Request for authentication via app
POST /api/auth/start HTTP/1.1
Host: onboardingdev.taktikal.is
Accept: application/json
Content-Type: application/json
{
"Ssn": "1234567890",
"FlowKey": "abc123abc123",
"AuthenticationContextType": "App"
}
## Response
{
"authRequestId": "1-147rag342352345",
"verificationCode": "1234",
"pollingInterval": 3
}
Parameters
flowKeyis a key provided by Taktikal. You can also retrieve a list of your flows via aGETrequest here/api/management/flowSsnis the Icelandic social security number of the individual you wish to start the authentication process forPhoneNumberis the mobile number of the individual you wish to start the authentication process forAuthenticationContextTypeis eitherSimorAppwhich decides the authentication method the individual will use to authenticateIncludeVerificationCode(default false) if set to true we will also return aVerificationCodefor Sim Auth
Response parameters
verifcationCodewhich the user will see in their device and we strongly recommend to display on your end. This will be empty for sim auth unless you set the parameterIncludeVerificationCodetotrueauthRequestIdis a unique key for this login session, you will need to send with every call to/api/auth/poll(see below) until the user logs in or the request ends (see Expiry and polling window).pollingIntervaldefines interval between calls to/api/auth/pollin seconds. e.g. if set to 4 you cannot call/api/auth/pollmore than once every 4 seconds.
Checking if a user has authenticated
To check if a user has authenticated you need to send a request to /api/auth/poll
Parameters
authRequestId: The unique key for this login session that you received in the call to/api/auth/startflowKey: The same flowkey that was used to call/api/auth/startLookupType: The/api/auth/pollrequest returns aCustomerobject but with different amount of information filled out depending on theLookupType. IfLookupType.NameAddressFamilyis selected then the extra values returned will be returned inMeta
There are three different return types depending on the LookupType
Name: Will only return the SSN and Name of the authenticated person. This has no additional lookup cost. This is the default value if no value is given forLookupType.NameAddress: Will return Name, SSN and legal address information. This has an additional lookup cost.NameAddressFamily: Returns Name, SSN, Address data, gender code, and family information. Note: Family lookup requires special permission.
Example request to poll authentication status of user
## Request for polling
POST /api/auth/poll HTTP/1.1
Host: onboardingdev.taktikal.is
Accept: application/json
Content-Type: application/json
{
"authRequestId": "1-147rag342352345",
"FlowKey": "abc123abc123",
"LookupType": "NameAddressFamily"
}
## Response
{
"waitingForUserInput": false, //true if still waiting for user to authenticate
"statusMessage": "Ok",
"customer": {
"name": "Test User",
"ssn": "1234567890",
"phoneNumber": "1234567",
"address": "Address 5",
"postalCode": "555",
"city": "Gotham City",
"token": "f19b032a237e4fbc94f3",
"flowKey": "abc123abc123",
"meta": {
"ParnerSsn": "1231231245",
"FamilyNumber": "1234567890",
"GenderCode": "1",
"FamilyStatus": "3"
}
}
}
Examples code in different languages
- Node.js
- C#
- PHP
const axios = require("axios");
const flowKey = "abc123abc123";
const start = async () => {
const { data } = await axios.post(
"https://onboardingdev.taktikal.is/api/auth/start",
{
phoneNumber: "1234567",
flowKey,
authenticationContextType: "Sim"
}
);
return data;
};
const poll = async (authRequestId) => {
const { data } = await axios.post(
"https://onboardingdev.taktikal.is/api/auth/poll",
{
authRequestId,
flowKey,
lookupType: "Name"
}
);
return data;
};
const startAndPollAuthentication = async () => {
const { authRequestId } = await start();
while (true) {
await new Promise((r) => setTimeout(r, 2000));
const { customer, waitingForUserInput } = await poll(authRequestId);
if (!waitingForUserInput) {
console.log(customer);
process.exit(0);
}
}
};
startAndPollAuthentication();
var httpClient = new System.Net.Http.HttpClient();
var resp = await httpClient.PostAsJsonAsync("https://onboardingdev.taktikal.is/api/auth/start", new
{
FlowKey = "abc123abc123",
PhoneNumber = "1234567",
AuthenticationContextType = AuthenticationContextType.Sim // or just "Sim" if you don't want to use an enum
});
var startAuthResponse = await resp.Content.ReadFromJsonAsync<StartAuthResponse>();
while (true)
{
await Task.Delay(2000);
var pollResponse = await httpClient.PostAsJsonAsync("https://onboardingdev.taktikal.is/api/auth/poll", new
{
FlowKey = "abc123abc123",
AuthRequestId = startAuthResponse.AuthRequestId,
LookupType = LookupType.NameAddressFamily // or just "NameAddressFamily" if you don't want to use an enum
});
if (pollResponse.IsErrorResponse())
{
// log error
break;
}
var parsedResponse = await pollResponse.Content.ReadFromJsonAsync<PollCustomer>();
if (!parsedResponse.WaitingForUserInput)
{
// access customer object here parsedResponse.Customer
break;
}
}
public class StartAuthResponse
{
public string AuthRequestId { get; set; }
public string VerificationCode { get; set; }
}
public class PollCustomer
{
public Customer Customer { get; set; }
public string StatusMessage { get; set; }
public bool WaitingForUserInput { get; set; } = false;
}
public class Customer
{
public string Name { get; set; }
public string Ssn { get; set; }
public string PhoneNumber { get; set; }
public string Email { get; set; }
public string Address { get; set; }
public string PostalCode { get; set; }
public string City { get; set; }
public string Token { get; set; }
public string FlowKey { get; set; }
public Dictionary<string,string> Meta { get; set; }
}
<?php
$request = new HttpRequest();
$request->setUrl('https://onboardingdev.taktikal.is/api/auth/start');
$request->setMethod(HTTP_METH_POST);
$request->setHeaders(array(
'Host' => 'onboardingdev.taktikal.is',
'Content-Type' => 'application/json',
'Accept' => 'application/json'
));
$request->setBody('{
"PhoneNumber": "1234567",
"FlowKey": "abc123abc123",
"AuthenticationContextType": "Sim"
}');
try {
$response = $request->send();
$startAuthResponse = $response->getBody();
while(true) {
sleep(2)
$request->setBody('{
"AuthRequestId": startAuthResponse.authRequestId,
"FlowKey": "abc123abc123",
"LookupType": "NameAddressFamily"
}');
$response = $request->send();
$pollResponse = $response->getBody();
if(!$pollResponse.waitingForUserInput) {
echo $pollResponse
break;
}
}
} catch (HttpException $ex) {
echo $ex;
}
Poll outcomes
Every call to /api/auth/poll ends in one of the outcomes below.
| Outcome | HTTP status | errorCode | Body |
|---|---|---|---|
| Still waiting for the user | 200 | - | waitingForUserInput: true, statusMessage: "Beðið eftir að notandi auðkenni sig", no customer |
| Authenticated | 200 | - | customer filled in, statusMessage: "Ok" |
| Request expired | 403 | authentication.error.audkenni.forbidden | responseStatus.message: Rann út á tíma |
| Cancelled by the user | 403 | authentication.error.audkenni.forbidden | responseStatus.message: Notandi hætti við auðkenningu. |
| Other failure reported by the ID provider | 403 | authentication.error.audkenni.forbidden | responseStatus.message with the reason, for example Villa í innri kerfum (provider internal error) or Villa í samskiptum (communication error) |
| Error on our side while checking the request | 500 | authentication.error.audkenni.poll | responseStatus.message with the details |
Error responses have this shape:
{
"responseStatus": {
"errorCode": "authentication.error.audkenni.forbidden",
"message": "Notandi hætti við auðkenningu.",
"errors": []
}
}
A 200 with waitingForUserInput: false and no customer (statusMessage: "Processing") means we are still preparing the result.
Keep polling.
Telling a timeout from a cancel
A timeout and a cancel both return 403 with the same errorCode, authentication.error.audkenni.forbidden.
Only responseStatus.message differs:
responseStatus.message | Meaning |
|---|---|
Rann út á tíma | The request expired before the user answered |
Notandi hætti við auðkenningu. | The user cancelled the authentication |
These two texts are the contract for telling the cases apart.
Match them exactly, including the final period in Notandi hætti við auðkenningu..
Other messages describe other failures, for example Notandi fannst ekki (user not found) or the generic Því miður tókst ekki að framkvæma umbeðna aðgerð. Vinsamlega reynið aftur.
New messages can appear, so fall back to a generic "please try again" message for any text you do not recognise.
Any 403 from /api/auth/poll is final.
Do not keep polling the same authRequestId after one, and start a new authentication with /api/auth/start instead.
Failure to start
/api/auth/start can also fail with a 403 and errorCode authentication.error.audkenni.start.
For example, responseStatus.message is Notandi fannst ekki when the person has no electronic ID for that phone number or social security number.
No authRequestId is returned in this case, so there is nothing to poll.
Expiry and polling window
How long an unanswered request stays open is decided by the electronic ID provider, not by us. These durations are approximate:
| Method | An unanswered request expires after |
|---|---|
Sim | about 90 seconds, sooner if the phone cannot be reached |
App | about 2 minutes |
On our side a request is kept for at most 5 minutes after /api/auth/start.
An authentication that is approved later than that cannot be completed.
We recommend:
- Poll every
pollingIntervalseconds, as returned by/api/auth/start. - Stop on the first final answer: a
200withcustomerfilled in, or any4xx/5xx. - Use a safety ceiling of about 3 minutes in total.
A shorter cut-off, such as 60 seconds, ends some successful
Simauthentications before the user has answered.
Old method
Example request to authenticate a person
## Request
POST /api/auth HTTP/1.1
Host: onboardingdev.taktikal.is
Accept: application/json
Content-Type: application/json
{
"PhoneNumber": "1234567",
"FlowKey": "abc123abc123",
"LookupType": "NameAddressFamily"
}
## Response
{
"name": "Test User",
"ssn": "1234567890",
"phoneNumber": "1234567",
"address": "Address 5",
"postalCode": "555",
"city": "Gotham City",
"token": "f19b032a237e4fbc94f3",
"flowKey": "abc123abc123",
"meta": {
"ParnerSsn": "1231231245",
"FamilyNumber": "1234567890",
"GenderCode": "1",
"FamilyStatus": "3"
}
}