Code copied successfuly

Silent Authentication API

Version: v1.0.0

Specification Release Notes Other versions

Silent Authentication API provides OAuth2-based authentication service that verifies whether an authentication request originates from the same subscriber by resolving network session information.

Typical flow:

  1. Client initiates OAuth2 authorization with /oauth2/authorize using login_hint containing the MSISDN.
  2. The service extracts MSISDN, client IP/port and timestamp, then resolves network session data.
  3. The service checks if the provided MSISDN matches the one associated with the resolved network session.
  4. Client exchanges the code for access token via /oauth2/token.
  5. The access token contains a mobile_id claim when verification succeeds.
  6. Optionally, client can call /oauth2/userinfo with the access token to retrieve verification results without decoding the JWT.

Base URLs

OAuth2

OAuth2 endpoints for silent authentication flow.

OAuth2 authorization

Code samples

# You can also use wget
curl -X GET 'https://api.tyntec.com/silent-auth/v1/oauth2/authorize?response_type=code& \
  client_id=client& \
  state=xyz12345& \
  scope=openid%20tt%3Aphone_verify& \
  redirect_uri=https%3A%2F%2Fclient.example.com%2Fcallback'

GET https://api.tyntec.com/silent-auth/v1/oauth2/authorize?response_type=code&client_id=client&state=xyz12345&scope=openid%20tt%3Aphone_verify&redirect_uri=https%3A%2F%2Fclient.example.com%2Fcallback HTTP/1.1
Host: api.tyntec.com


fetch('https://api.tyntec.com/silent-auth/v1/oauth2/authorize?response_type=code&client_id=client&state=xyz12345&scope=openid%20tt%3Aphone_verify&redirect_uri=https%3A%2F%2Fclient.example.com%2Fcallback',
{
  method: 'GET'

})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

require 'rest-client'
require 'json'

result = RestClient.get 'https://api.tyntec.com/silent-auth/v1/oauth2/authorize',
  params: {
  'response_type' => 'string',
'client_id' => 'string',
'state' => 'string',
'scope' => 'string',
'redirect_uri' => 'string(uri)'
}

p JSON.parse(result)

import requests

r = requests.get('https://api.tyntec.com/silent-auth/v1/oauth2/authorize', params={
  'response_type': 'code',  'client_id': 'client',  'state': 'xyz12345',  'scope': 'openid tt:phone_verify',  'redirect_uri': 'https://client.example.com/callback'
)

print(r.json())

<?php

require 'vendor/autoload.php';

$client = new \GuzzleHttp\Client();

// Define array of request body.
$request_body = array();

try {
    $response = $client->request('GET','https://api.tyntec.com/silent-auth/v1/oauth2/authorize', array(
        'headers' => $headers,
        'json' => $request_body,
       )
    );
    print_r($response->getBody()->getContents());
 }
 catch (\GuzzleHttp\Exception\BadResponseException $e) {
    // handle exception or api errors.
    print_r($e->getMessage());
 }

 // ...

URL obj = new URL("https://api.tyntec.com/silent-auth/v1/oauth2/authorize?response_type=code&client_id=client&state=xyz12345&scope=openid%20tt%3Aphone_verify&redirect_uri=https%3A%2F%2Fclient.example.com%2Fcallback");
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("GET");

int responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(
    new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
    response.append(inputLine);
}
in.close();
System.out.println(response.toString());

package main

import (
       "bytes"
       "net/http"
)

func main() {

    data := bytes.NewBuffer([]byte{jsonReq})
    req, err := http.NewRequest("GET", "https://api.tyntec.com/silent-auth/v1/oauth2/authorize", data)
    req.Header = headers

    client := &http.Client{}
    resp, err := client.Do(req)
    // ...
}

GET /oauth2/authorize

Initiates the OAuth2 authorization flow with silent authentication. The client provides the subscriber's MSISDN in the login_hint parameter. The service resolves the network session and verifies the MSISDN, then redirects to the callback URI with an authorization code.

Parameters

Name In Type Required Description
response_type query string true OAuth2 response type (must be 'code')
client_id query string true Client identifier, provided during the onboarding process
state query string true Opaque value used by the client to maintain state between request and callback
scope query string true Space-delimited scope values.
redirect_uri query string(uri) true Client callback URI where the authorization code will be sent
login_hint query string false Phone number in the international format with or without the leading “+” or “00” prefix
Detailed description

scope: Space-delimited scope values.

Possible values:

  • tt:phone_verify
  • tt:mobile_id

login_hint: Phone number in the international format with or without the leading “+” or “00” prefix

Examples: +491234567890, 00491234567890 or 491234567890.

Client should provide the login_hint for higher security and better user experience, but it is not mandatory. If the login_hint is not provided, a pop-up window will be shown to the user to input their MSISDN.

Enumerated values
Parameter Value
response_type code

Responses

Status Meaning Description Schema
302 Found Redirects the user-agent to the client-provided callback URI with OAuth 2.0 parameters.

Success case:

Returns authorization code and state in query parameters.

Example: https://client.example.com/callback?code=9oubNbRC9el6-KoSda3NvBQlf3-Bh...&state=xyz12345

Error cases:

Returns error, error_description, and state in query parameters. Query parameter values are URL-encoded (spaces may appear as "+" in application/x-www-form-urlencoded encoding).

Error parameter names follow OAuth 2.0 specification:

  • error: Error code (e.g., invalid_request, invalid_scope, no_data_session)
  • error_description: Human-readable error explanation
  • state: Opaque value from the authorization request

Example: https://client.example.com/callback?error=invalid_request&error_description=OAuth+2.0+Parameter:+client_id&state=xyz12345|None|

Response Headers

Status Header Type Format Description
302 Location string uri Redirect URI with query parameters. For success: code and state. For errors: error, error_description, and state.

OAuth2 token

Code samples

curl -X POST "https://api.tyntec.com/silent-auth/v1/oauth2/token" \
  -H "Content-Type: application/x-www-form-urlencoded" \
  -H "apiKey: <apiKey>" \
  --data-urlencode "grant_type=authorization_code" \
  --data-urlencode "code=9oubNbRC9el6-KoSda3NvBQlf3-Bh..." \
  --data-urlencode "redirect_uri=https://client.example.com/callback" \
  --data-urlencode "client_id=client" \
  --data-urlencode "client_secret=12345"

import requests

url = "https://api.tyntec.com/silent-auth/v1/oauth2/token"
headers = {
    "Content-Type": "application/x-www-form-urlencoded",
    "apiKey": "<apiKey>"
}
data = {
    "grant_type": "authorization_code",
    "code": "9oubNbRC9el6-KoSda3NvBQlf3-Bh...",
    "redirect_uri": "https://client.example.com/callback",
    "client_id": "client",
    "client_secret": "12345"
}

response = requests.post(url, headers=headers, data=data)
print(response.json())

const data = new URLSearchParams({
  grant_type: 'authorization_code',
  code: '9oubNbRC9el6-KoSda3NvBQlf3-Bh...',
  redirect_uri: 'https://client.example.com/callback',
  client_id: 'client',
  client_secret: '12345'
});

fetch('https://api.tyntec.com/silent-auth/v1/oauth2/token', {
  method: 'POST',
  headers: {
    'Content-Type': 'application/x-www-form-urlencoded',
    'apiKey': '<apiKey>'
  },
  body: data
})
.then(response => response.json())
.then(data => console.log(data));

import java.io.*;
import java.net.*;
import java.nio.charset.StandardCharsets;

URL url = new URL("https://api.tyntec.com/silent-auth/v1/oauth2/token");
HttpURLConnection conn = (HttpURLConnection) url.openConnection();
conn.setRequestMethod("POST");
conn.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
conn.setRequestProperty("apiKey", "<apiKey>");
conn.setDoOutput(true);

String data = "grant_type=authorization_code" +
    "&code=9oubNbRC9el6-KoSda3NvBQlf3-Bh..." +
    "&redirect_uri=https://client.example.com/callback" +
    "&client_id=client" +
    "&client_secret=12345";

try (OutputStream os = conn.getOutputStream()) {
    byte[] input = data.getBytes(StandardCharsets.UTF_8);
    os.write(input, 0, input.length);
}

try (BufferedReader br = new BufferedReader(
        new InputStreamReader(conn.getInputStream(), StandardCharsets.UTF_8))) {
    StringBuilder response = new StringBuilder();
    String responseLine;
    while ((responseLine = br.readLine()) != null) {
        response.append(responseLine.trim());
    }
    System.out.println(response.toString());
}

POST /silent-auth/v1/oauth2/token HTTP/1.1
Host: api.tyntec.com
Content-Type: application/x-www-form-urlencoded
apiKey: <apiKey>

grant_type=authorization_code&code=9oubNbRC9el6-KoSda3NvBQlf3-Bh...&redirect_uri=https://client.example.com/callback&client_id=client&client_secret=12345

require 'net/http'
require 'uri'
require 'json'

uri = URI.parse("https://api.tyntec.com/silent-auth/v1/oauth2/token")
request = Net::HTTP::Post.new(uri)
request.content_type = "application/x-www-form-urlencoded"
request["apiKey"] = "<apiKey>"

request.set_form_data(
  "grant_type" => "authorization_code",
  "code" => "9oubNbRC9el6-KoSda3NvBQlf3-Bh...",
  "redirect_uri" => "https://client.example.com/callback",
  "client_id" => "client",
  "client_secret" => "12345"
)

response = Net::HTTP.start(uri.hostname, uri.port, use_ssl: true) do |http|
  http.request(request)
end

puts JSON.parse(response.body)

package main

import (
    "fmt"
    "io"
    "net/http"
    "net/url"
    "strings"
)

func main() {
    apiUrl := "https://api.tyntec.com/silent-auth/v1/oauth2/token"

    data := url.Values{}
    data.Set("grant_type", "authorization_code")
    data.Set("code", "9oubNbRC9el6-KoSda3NvBQlf3-Bh...")
    data.Set("redirect_uri", "https://client.example.com/callback")
    data.Set("client_id", "client")
    data.Set("client_secret", "12345")

    req, err := http.NewRequest("POST", apiUrl, strings.NewReader(data.Encode()))
    if err != nil {
        panic(err)
    }

    req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
    req.Header.Set("apiKey", "<apiKey>")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        panic(err)
    }
    defer resp.Body.Close()

    body, err := io.ReadAll(resp.Body)
    if err != nil {
        panic(err)
    }

    fmt.Println(string(body))
}

<?php

$url = "https://api.tyntec.com/silent-auth/v1/oauth2/token";

$data = array(
    'grant_type' => 'authorization_code',
    'code' => '9oubNbRC9el6-KoSda3NvBQlf3-Bh...',
    'redirect_uri' => 'https://client.example.com/callback',
    'client_id' => 'client',
    'client_secret' => '12345'
);

$options = array(
    'http' => array(
        'header'  => "Content-Type: application/x-www-form-urlencoded\r\n" .
                     "apiKey: <apiKey>\r\n",
        'method'  => 'POST',
        'content' => http_build_query($data)
    )
);

$context  = stream_context_create($options);
$result = file_get_contents($url, false, $context);

if ($result === FALSE) {
    die('Error');
}

$response = json_decode($result, true);
print_r($response);

?>

POST /oauth2/token

Exchange an authorization code for access token and refresh token. The access token contains a mobile_id claim when silent authentication verification succeeded.

Parameters

Name In Type Required Description
body body object true none
» grant_type body string true OAuth2 grant type (must be 'authorization_code')
» code body string true Authorization code received from /oauth2/authorize
» redirect_uri body string(uri) true Redirect URI that was used in the authorization request
» client_id body string true Client identifier
» client_secret body string true Client secret

Example responses

Successful token response

{
  "access_token": "eyJraWQiOiIyYzc5NDNmMy00YzIyLTQ5...",
  "refresh_token": "DawkDPIXqUDqyjHfzcifAsc8gvL1jBys...",
  "scope": "openid tt:mobile_id",
  "token_type": "Bearer",
  "expires_in": 86399
}

400 Response

{
  "error": "invalid_request",
  "error_description": "Client specified an invalid argument, request body or query param."
}

Responses

Status Meaning Description Schema
200 OK Successful token response TokenResponse
400 Bad Request Bad Request - Invalid request parameters BadRequestError
401 Unauthorized Unauthorized - Invalid client credentials or authorization code UnauthorizedError
403 Forbidden Forbidden PermissionDeniedError
500 Internal Server Error Internal Server Error InternalServerError

OAuth2 UserInfo

Code samples

# You can also use wget
curl -X GET 'https://api.tyntec.com/silent-auth/v1/oauth2/userinfo' \
  -H 'Accept: application/json' \
  -H 'Authorization: Bearer {access-token}'

GET https://api.tyntec.com/silent-auth/v1/oauth2/userinfo HTTP/1.1
Host: api.tyntec.com
Accept: application/json
Authorization: Bearer {access-token}


const headers = {
  'Accept':'application/json',
  'Authorization':'Bearer {access-token}'

};

fetch('https://api.tyntec.com/silent-auth/v1/oauth2/userinfo',
{
  method: 'GET',

  headers: headers
})
.then(function(res) {
    return res.json();
}).then(function(body) {
    console.log(body);
});

require 'rest-client'
require 'json'

headers = {
  'Accept' => 'application/json',
  'Authorization' => 'Bearer {access-token}'
}

result = RestClient.get 'https://api.tyntec.com/silent-auth/v1/oauth2/userinfo',
  params: {
  }, headers: headers

p JSON.parse(result)

import requests
headers = {
  'Accept': 'application/json',
  'Authorization': 'Bearer {access-token}'
}

r = requests.get('https://api.tyntec.com/silent-auth/v1/oauth2/userinfo', params={

}, headers = headers)

print(r.json())

<?php

require 'vendor/autoload.php';

$headers = array(
    'Accept' => 'application/json',
    'Authorization' => 'Bearer {access-token}',

    );

$client = new \GuzzleHttp\Client();

// Define array of request body.
$request_body = array();

try {
    $response = $client->request('GET','https://api.tyntec.com/silent-auth/v1/oauth2/userinfo', array(
        'headers' => $headers,
        'json' => $request_body,
       )
    );
    print_r($response->getBody()->getContents());
 }
 catch (\GuzzleHttp\Exception\BadResponseException $e) {
    // handle exception or api errors.
    print_r($e->getMessage());
 }

 // ...

URL obj = new URL("https://api.tyntec.com/silent-auth/v1/oauth2/userinfo");
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("GET");
con.setRequestProperty("Accept", "application/json");
con.setRequestProperty("Authorization", "Bearer {access-token}");

int responseCode = con.getResponseCode();
BufferedReader in = new BufferedReader(
    new InputStreamReader(con.getInputStream()));
String inputLine;
StringBuffer response = new StringBuffer();
while ((inputLine = in.readLine()) != null) {
    response.append(inputLine);
}
in.close();
System.out.println(response.toString());

package main

import (
       "bytes"
       "net/http"
)

func main() {

    headers := map[string][]string{
        "Accept": []string{"application/json"},
        "Authorization": []string{"Bearer {access-token}"},

    }

    data := bytes.NewBuffer([]byte{jsonReq})
    req, err := http.NewRequest("GET", "https://api.tyntec.com/silent-auth/v1/oauth2/userinfo", data)
    req.Header = headers

    client := &http.Client{}
    resp, err := client.Do(req)
    // ...
}

GET /oauth2/userinfo

Retrieve authentication results using the access token from the token endpoint. This standard OpenID Connect UserInfo endpoint returns the mobile_id, phone number verification status, and user identifier.

Clients can call this endpoint with Bearer token authentication instead of manually decoding the access_token JWT.

Example responses

Successful userinfo response

{
  "sub": "99dd91d1-c949-433c-bd9e-0682eb6d6d26",
  "mobile_id": "2d83f2c1f618313f4b24516913d4e45e00ee289af2bc428d897b175adc5775e643e52bc7286eeca02353e4eb99fccacbe263ec922dbfdde6ed64be90f0e2f780",
  "login_hint": "+491123456789",
  "phone_number_verified": "true"
}
{
  "sub": "anonymous",
  "mobile_id": "2d83f2c1f618313f4b24516913d4e45e00ee289af2bc428d897b175adc5775e643e52bc7286eeca02353e4eb99fccacbe263ec922dbfdde6ed64be90f0e2f780",
  "login_hint": "+491123456789",
  "phone_number_verified": "false"
}

401 Response

{
  "error": "invalid_client",
  "error_description": "Request not authenticated due to missing, invalid, or expired credentials. A new authentication is required."
}

Responses

Status Meaning Description Schema
200 OK Successful userinfo response UserInfoResponse
401 Unauthorized Unauthorized - Invalid or expired access token UnauthorizedError
403 Forbidden Forbidden PermissionDeniedError
500 Internal Server Error Internal Server Error InternalServerError

Schemas

CallbackUri

"https://client.example.com/callback?code=9oubNbRC9el6-KoSda3NvBQlf3-Bh...&state=xyz12345"

Client callback URI where the authorization code will be sent

Properties

Name Type Required Restrictions Description
anonymous string(uri) false none Client callback URI where the authorization code will be sent

TokenResponse

{
  "access_token": "eyJraWQiOiIyYzc5NDNmMy00YzIyLTQ5MzUtOTBmZi1kOWVkNTVmYTYxN2QiLCJhbGciOiJSUzI1NiJ9...",
  "refresh_token": "DawkDPIXqUDqyjHfzcifAsc8gvL1jBysWTe4vgXy1PbJGk51Ec6mRIZnCJh7nzRBG1MnE7C...",
  "scope": "openid tt:mobile_id tt:phone_verify",
  "token_type": "Bearer",
  "expires_in": 86399
}

Properties

Name Type Required Restrictions Description
access_token string false none OAuth2 access token (JWT format) containing the mobile_id claim when verification succeeded
refresh_token string false none OAuth2 refresh token
scope string false none Space-delimited scope values granted
token_type string false none Token type (always 'Bearer')
expires_in integer false none Access token lifetime in seconds

UserInfoResponse

{
  "sub": "99dd91d1-c949-433c-bd9e-0682eb6d6d26",
  "mobile_id": "2d83f2c1f618313f4b24516913d4e45e00ee289af2bc428d897b175adc5775e643e52bc7286eeca02353e4eb99fccacbe263ec922dbfdde6ed64be90f0e2f780",
  "login_hint": "+491123456789",
  "phone_number_verified": "true"
}

Properties

Name Type Required Restrictions Description
sub string true none Subject identifier is a UUID of the request. Returns 'anonymous' when verification fails (provided MSISDN does not match the resolved MSISDN).
mobile_id string true none Mobile ID can be used as a network identifier of a user.
login_hint string true none Phone number value that was submitted in the authorization request.
phone_number_verified string true none Boolean value indicating whether the submitted phone number matches the resolved network MSISDN.
Enumerated values
Property Value
phone_number_verified true
phone_number_verified false

BadRequestError

{
  "error": "invalid_request",
  "error_description": "Client specified an invalid argument, request body or query param."
}

Properties

Name Type Required Restrictions Description
error string false none Error code
error_description string false none Error message

UnauthorizedError

{
  "error": "invalid_client",
  "error_description": "Request not authenticated due to missing, invalid, or expired credentials. A new authentication is required."
}

Properties

Name Type Required Restrictions Description
error string false none Error code
error_description string false none Error message

PermissionDeniedError

{
  "error": "permission_denied",
  "error_description": "Client does not have sufficient permissions to perform this action."
}

Properties

Name Type Required Restrictions Description
error string false none Error code
error_description string false none Error message

NotFoundError

{
  "error": "not_found",
  "error_description": "The specified resource is not found."
}

Properties

Name Type Required Restrictions Description
error string false none Error code
error_description string false none Error message

InternalServerError

{
  "error": "internal_server_error",
  "error_description": "Request could not be processed"
}

Properties

Name Type Required Restrictions Description
error string false none Error code
error_description string false none Error message