Silent Authentication API
Version: v1.0.0
Specification Release Notes Other versions
Silent Authentication API provides OAuth2-based authentication that verifies whether an authentication request originates from the same subscriber by resolving network session information.
Typical flow:
- Client initiates OAuth2 authorization with
/oauth2/authorizeusing login_hint containing the MSISDN. - The service extracts MSISDN, client IP/port and timestamp, then resolves network session data.
- Check if the provided MSISDN matches the one associated with the resolved network session.
- Client exchanges the code for access/ID tokens via
/oauth2/token. - The ID token contains a
mobile_idclaim when verification succeeds.
Base URLs
OAuth2
OAuth2 endpoints for silent authentication flow. Supports authorization code grant type with network-based subscriber verification.
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&login_hint=301234567890
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&login_hint=301234567890 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&login_hint=301234567890',
{
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)',
'login_hint' => 'string'
}
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', 'login_hint': '301234567890'
)
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&login_hint=301234567890");
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 code 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 returns:
- an authorization code and the user ID as a
subclaim, if the MSISDN matches the one associated with the network session, or - an authorization code and
anonymoussubclaim, if the MSISDN does not match.
Parameters
| Name | In | Type | Required | Description |
|---|---|---|---|---|
| response_type | query | string | true | OAuth2 response type (must be 'code') |
| client_id | query | string | true | Client identifier |
| 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 (e.g., 'openid tt:phone_verify') |
| redirect_uri | query | string(uri) | true | Client callback URI where the authorization code will be sent |
| login_hint | query | string | true | Subscriber MSISDN (without leading plus, e.g., 301234567890) |
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.
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 explanationstate: Opaque value from the authorization request|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
# You can also use wget
curl -X POST https://api.tyntec.com/silent-auth/v1/oauth2/token \
-H 'Content-Type: application/x-www-form-urlencoded' \
-H 'Accept: application/json'
POST https://api.tyntec.com/silent-auth/v1/oauth2/token HTTP/1.1
Host: api.tyntec.com
Content-Type: application/x-www-form-urlencoded
Accept: application/json
const inputBody = '{
"grant_type": "authorization_code",
"code": "kgoV1pFxi1EIlocesrMcia6AhNyw6...",
"redirect_uri": "https://client.example.com/callback",
"client_id": "client",
"client_secret": "12345"
}';
const headers = {
'Content-Type':'application/x-www-form-urlencoded',
'Accept':'application/json'
};
fetch('https://api.tyntec.com/silent-auth/v1/oauth2/token',
{
method: 'POST',
body: inputBody,
headers: headers
})
.then(function(res) {
return res.json();
}).then(function(body) {
console.log(body);
});
require 'rest-client'
require 'json'
headers = {
'Content-Type' => 'application/x-www-form-urlencoded',
'Accept' => 'application/json'
}
result = RestClient.post 'https://api.tyntec.com/silent-auth/v1/oauth2/token',
params: {
}, headers: headers
p JSON.parse(result)
import requests
headers = {
'Content-Type': 'application/x-www-form-urlencoded',
'Accept': 'application/json'
}
r = requests.post('https://api.tyntec.com/silent-auth/v1/oauth2/token', params={
}, headers = headers)
print(r.json())
<?php
require 'vendor/autoload.php';
$headers = array(
'Content-Type' => 'application/x-www-form-urlencoded',
'Accept' => 'application/json',
);
$client = new \GuzzleHttp\Client();
// Define array of request body.
$request_body = array();
try {
$response = $client->request('POST','https://api.tyntec.com/silent-auth/v1/oauth2/token', 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/token");
HttpURLConnection con = (HttpURLConnection) obj.openConnection();
con.setRequestMethod("POST");
con.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");
con.setRequestProperty("Accept", "application/json");
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{
"Content-Type": []string{"application/x-www-form-urlencoded"},
"Accept": []string{"application/json"},
}
data := bytes.NewBuffer([]byte{jsonReq})
req, err := http.NewRequest("POST", "https://api.tyntec.com/silent-auth/v1/oauth2/token", data)
req.Header = headers
client := &http.Client{}
resp, err := client.Do(req)
// ...
}
POST /oauth2/token
Exchange an authorization code for access token, ID token, and refresh token. The ID token
includes a mobile_id claim when silent authentication verification succeeded.
Body parameter
grant_type: authorization_code
code: kgoV1pFxi1EIlocesrMcia6AhNyw6...
redirect_uri: https://client.example.com/callback
client_id: client
client_secret: "12345"
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 |
Enumerated values
| Parameter | Value |
|---|---|
| » grant_type | authorization_code |
Example responses
Successful token response
{
"access_token": "eyJraWQiOiIyYzc5NDNmMy00YzIyLTQ5...",
"refresh_token": "DawkDPIXqUDqyjHfzcifAsc8gvL1jBys...",
"scope": "tt:mobile_id openid",
"id_token": "eyJraWQiOiIyYzc5NDNmMy00YzIyLTQ5MzUtO...",
"token_type": "Bearer",
"expires_in": 86399
}
400 Response
{
"status": 400,
"code": "INVALID_ARGUMENT",
"message": "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 | InvalidArgumentError |
| 401 | Unauthorized | Unauthorized - Invalid client credentials or authorization code | UnauthenticatedError |
| 403 | Forbidden | Forbidden | PermissionDeniedError |
| 404 | Not Found | Not Found | NotFoundError |
| 500 | Internal Server Error | Internal Server Error | InternalServerError |
Schemas
TokenResponse
{
"access_token": "eyJraWQiOiIyYzc5NDNmMy00YzIyLTQ5MzUtOTBmZi1kOWVkNTVmYTYxN2QiLCJhbGciOiJSUzI1NiJ9...",
"refresh_token": "DawkDPIXqUDqyjHfzcifAsc8gvL1jBysWTe4vgXy1PbJGk51Ec6mRIZnCJh7nzRBG1MnE7C...",
"scope": "tt:mobile_id openid",
"id_token": "eyJraWQiOiIyYzc5NDNmMy00YzIyLTQ5MzUtOTBmZi1kOWVkNTVmYTYxN2QiLCJhbGciOiJSUzI1NiJ9...",
"token_type": "Bearer",
"expires_in": 86399
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| access_token | string | false | none | OAuth2 access token (JWT format) |
| refresh_token | string | false | none | OAuth2 refresh token |
| scope | string | false | none | Space-delimited scope values granted |
| id_token | string | false | none | OpenID Connect ID token (JWT format) containing mobile_id claim when verification succeeded |
| token_type | string | false | none | Token type (always 'Bearer') |
| expires_in | integer | false | none | Access token lifetime in seconds |
InvalidArgumentError
{
"status": 400,
"code": "INVALID_ARGUMENT",
"message": "Client specified an invalid argument, request body or query param."
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| status | integer | true | none | HTTP status code |
| code | string | true | none | Error code |
| message | string | true | none | Error message |
OutOfRangeError
{
"status": 400,
"code": "OUT_OF_RANGE",
"message": "Client specified an invalid range."
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| status | integer | true | none | HTTP status code |
| code | string | true | none | Error code |
| message | string | true | none | Error message |
UnauthenticatedError
{
"status": 401,
"code": "UNAUTHENTICATED",
"message": "Request not authenticated due to missing, invalid, or expired credentials. A new authentication is required."
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| status | integer | true | none | HTTP status code |
| code | string | true | none | Error code |
| message | string | true | none | Error message |
PermissionDeniedError
{
"status": 403,
"code": "PERMISSION_DENIED",
"message": "Client does not have sufficient permissions to perform this action."
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| status | integer | true | none | HTTP status code |
| code | string | true | none | Error code |
| message | string | true | none | Error message |
NotFoundError
{
"status": 404,
"code": "NOT_FOUND",
"message": "The specified resource is not found."
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| status | integer | true | none | HTTP status code |
| code | string | true | none | Error code |
| message | string | true | none | Error message |
InternalServerError
{
"status": 500,
"code": "INTERNAL_SERVER_ERROR",
"message": "Request could not be processed"
}
Properties
| Name | Type | Required | Restrictions | Description |
|---|---|---|---|---|
| status | integer | true | none | HTTP status code |
| code | string | true | none | Error code |
| message | string | true | none | Error message |