Supporting Native Applications¶
Target Audience: Users, Developers, Stakeholders
Introduction¶
For the best user experience when implementing log in using the PW SSO2 on native iOS and Android applications, we suggest using the CreateSession endpoint to establish the SSO2 session directly from the application back-end. This way you can avoid using the normal Identify redirect method of establishing the session. This way there is no need to work with cookies in web-views for identifying the device.
The implementation flow is the same as specified here with the exception that a server-to-server call to CreateSession replaces the redirect to the Identify endpoint as well as the subsequent redirect back via the API user landing page.
Note
This implements login for a single application, users signed in to that application will not be logged in on other applications or websites implementing SSO2.
Create session¶
Description¶
The Create Session operation is used to establish a SSO session with a HTTP POST call. You create a Create Session JWT and post it to the SSO2-system.
The call should be made from the application backend and the session id in the response must be stored so that it can be used for further operations.
Request¶
The request to the Create Session endpoint is a server-to-server operation. You create a HTTP-POST-request with a JSON payload containing the encoded JWT. You sign the token with the secret related to the API-client that you use.
| Param | Description |
|---|---|
| t | Encoded JWT-token. |
curl --request POST \
--url https://payway-sso-stage.azurewebsites.net/createsession \
--header 'Content-Type: application/json' \
--data '{
"t": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJjaWQiOiI1NzIyMWYxMTU4NGQ1NGUyOGIwMDAwMDEiLCJpcGEiOiIxMjcuMC4wLjExIiwidWFzIjoiTXkgQXBwIDEuMCAtIGlvczEwIiwiYXBuIjoiTXkgQXBwIiwiYXB2IjoiMS4wIiwib3NuIjoiaU9TIiwib3N2IjoiMTAiLCJuYmYiOjE3MzI4NzkwOTAsImV4cCI6MTczMjk2NTQ5MCwiaWF0IjoxNzMyODc5MDkwLCJpc3MiOiJhZGVwcmltb3NlIiwiYXVkIjoicHctc3NvIn0.i_R7hBmOCARpimXz2PcdUTDNIQO2C75TgFFDDyans_I"
}'
The decoded JWT in the above example looks like this:
{
"alg": "HS256",
"typ": "JWT"
}
{
"cid": "57221f11584d54e28b000001",
"ipa": "127.0.0.11",
"uas": "My App 1.0 - ios10",
"apn": "My App",
"apv": "1.0",
"osn": "iOS",
"osv": "10",
"nbf": 1732879090,
"exp": 1732965490,
"iat": 1732879090,
"iss": "adeprimose",
"aud": "pw-sso"
}
| Claim | Type | Description |
|---|---|---|
| cid | string | "Client ID", Your API-client ID |
| ipa | string | "IP Address", the user's IP address. |
| apn | string | "Application name", your application name. Should match the application name in uas. |
| apv | string | "Application version", your application version. Should match the application version in uas. |
| osn | string | "Operating system name", your operating system name. Should match the operating system name in uas. |
| osv | string | "Operating system version", your operating system version. Should match the operating system version in uas. |
| uas | string | "User-Agent string", the application's User-Agent string. Used to show the application and device in the list of the user's sessions, see User-Agent string for native applications. |
| nbf | int(epoch) | "Not before", this should be the epoch timestamp of when this token is created. |
| exp | int(epoch) | "Expires", this should be the epoch timestamp for 10 seconds after token was created. |
| iat | int(epoch) | "Issued At", this should be the epoch timestamp of when this token is created. |
| iss | string | "Issuer", this should be your Payway Organisation ID. |
| aud | string | "Audience", this should always be "pw-sso". |
Endpoints:
| Environment | URI |
|---|---|
| Stage | https://payway-sso-stage.azurewebsites.net/createsession |
| Production | https://sso.worldoftulo.com/createsession |
User-Agent string for native applications¶
The application and device shown to the user in the list of active sessions (for example when session limitations prevent a login, or in "My sessions" in PCP) are derived from the User-Agent string sent in the uas claim. This applies to every request where your application sends a uas claim, not only Create Session.
Web browsers are recognised automatically. A native application is not, unless its User-Agent string follows this format:
<Application name> <Application version> - <Operating system name> <Operating system version>
| Part | Rules | Example |
|---|---|---|
| Application name | Free text, may contain spaces, slashes and non-ASCII characters. Must not contain ( or ). |
Example News / Sports |
| Application version | Numeric, at least two parts separated by dots. An optional leading v is allowed. |
3.2.1 |
| Separator | A dash surrounded by spaces: - |
|
| Operating system name | Letters and spaces only. | iOS, Android, iPadOS |
| Operating system version | Numeric, one or more parts separated by dots. | 17.5.1, 15 |
Examples of valid User-Agent strings:
Example News / Sports 3.2.1 - iOS 17.5.1
Example News / Sports 3.2.1 - Android 15
My App 2.0 - iPadOS 18.2
With Example News / Sports 3.2.1 - iOS 17.5.1 the session is presented as:
| Field in the session list | Value |
|---|---|
browser |
Example News / Sports 3.2.1 |
operating_system |
iOS 17 |
- The application version is shown in full, the operating system version with its major version only.
- A User-Agent string that doesn't follow the format is interpreted as a web browser User-Agent string. For a native application that usually means that the application can't be recognised, and the session is shown as an unknown device (
"browser": "N/A"). - Include the application version and the operating system version in
uas, and send the same string for every request from the same installation. It lets the user tell different devices and application versions apart.
Note
Changes to how a User-Agent string is interpreted only apply to sessions created after the change. Existing sessions keep the device information they were created with.
Response¶
The response to the create session request is a JSON payload containing an encoded JWT Session Token.
Example create session response:
{
"t": "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdHMiOiJhbm9uIiwic2lkIjoiZjBiYzE1ODgtN2ViMC1lZjExLTg4Y2YtMDAwZDNhYmMwNWRiIiwiYWlkIjoiIiwiYXQiOm51bGwsImVyciI6bnVsbCwiZW1zIjoiIiwiZnJmIjotMSwicmFhIjotMSwic2xtIjowLCJvdHAiOiIiLCJzZXMiOiIiLCJuYmYiOjE3MzI4Nzk1OTAsImV4cCI6MTczMjg3OTY1MCwiaWF0IjoxNzMyODc5NTkwLCJpc3MiOiJwdy1zc28iLCJhdWQiOiI1NzIyMWYxMTU4NGQ1NGUyOGIwMDAwMDEifQ.V2qkrAORBi7twpZh5y0F9MWS3Fe3V96kMEr80M60tj0"
}
The decoded JWT in the above example looks like this:
{
"sts": "anon",
"sid": "f0bc1588-7eb0-ef11-88cf-000d3abc05db",
"aid": "",
"at": null,
"err": null,
"ems": "",
"frf": -1,
"raa": -1,
"slm": 0,
"otp": "",
"ses": "",
"nbf": 1732879590,
"exp": 1732879650,
"iat": 1732879590,
"iss": "pw-sso",
"aud": "57221f11584d54e28b000001"
}
| Claim | Description |
|---|---|
| sts | Session state, will always be "anon" |
| sid | The session ID, a unique identifier for the SSO2 session. |
| aid | Payway account id, will always be empty |
| at | Authorization Ticket. Will always be null |
| err | If something went wrong this field will hold an error code to help figure out the problem. |
| ems | If something went wrong this field can hold an error message to help figure out the problem. |
| frf | Frozen for seconds, will always be -1 |
| raa | Remaining authentication attempts, will always be -1 |
| slm | The current session limit configured, see Session limitations |
| otp | One time password, will always be empty |
| ses | See Session limitations |
| nbf | This is the epoch timestamp of when this token is created. |
| exp | This is the epoch timestamp for 60 seconds after token was created. |
| iat | This is the epoch timestamp of when this token is created. |
| iss | Will always be issued by "pw-sso". |
| aud | The client_id of the API-client the token was issued for. |
Error codes¶
The error code can be found in the claim err. See general error codes for error codes not specific to a certain operation.
If a required claim is missing or empty in the request token, the response has HTTP status 401, err is invalid_token and the claim ems holds one of the following codes:
| Code | Description |
|---|---|
| invalid_ipa | The ipa claim is missing or empty. |
| invalid_uas | The uas claim is missing or empty. |
| invalid_apn | The apn claim is missing or empty. |
| invalid_apv | The apv claim is missing or empty. |
| invalid_osn | The osn claim is missing or empty. |
| invalid_osv | The osv claim is missing or empty. |