Overview
Extensions complement Gigya’s Webhooks and JavaScript Parameters to provide a powerful extensibility suite. Extensions support secure, server-side, synchronous execution of your code. Extensions enable quickly implementing your custom data validations and restrictions.
Using Extensions, you can meet a wide range of business use-cases, such as:
- Prevent a user from registering with an abusive username
- Prevent a user from registering with a disposable email address
- Validate that zip codes match country and state provided
To use Extensions, host custom functions on your site. Then, specify which Extension Endpoint (Gigya flow) to attach these functions to.
Your custom code should be hosted on a designated URL, and the Gigya Extension service will send an HTTPS POST request to that URL.
Extensions are currently only supported for full registered user accounts, and not available for use with Lite Accounts.
Adding a New Extension
After your code is prepared and hosted, set up the extension:
- Open the Extensions page of Gigya's Console.
- Click Add New Extension.
Give the extension a friendly name and select the API to which this extension is attached.
- Enter the URL where the extension code is hosted.
- Under Advanced, you can customize the following settings:
- Timeout: The extension timeout in milliseconds. If not specified, the default is 1000 ms. The acceptable range is 10 - 5000 ms.
- Fallback policy: Decide what happens in case of a technical error in the Extension execution. The default is to ignore the error, meaning the user flow will not be failed. You can choose FailOnAnyError to fail the user flow on any extension error. While this may cause legitimate flows to fail, it will ensure that no one is bypassing the extension logic.
- Save your changes.
- Activate or deactivate the extension as needed.
Extension Code
Overview
This section explains how the Extension POST requests are structured, and what the service expects to receive in response. The POST requests are done using a JWS. The following sections explain the structure of the JWS, the common payload shared by all extension points, and the specific parameters supported for each of the extension points.
General Notes
- For all extensions, you can configure the fallout policy: what to do in case of failure to get a valid response from the extension point for the specified timeout period. Choose between ignoring the error and letting the flow continue (e.g., the registration will continue as if the extension point was not declared), or failing all flows. In both cases, errors are logged.
- Any field that is "null", will not be sent.
- You can define one extension per each extension type, per site (or site group).
- You can use Extensions to return a variety of custom messages to the user, including a different message per language.
- You can use the same extension code to run a different scenario per each extension point, so that you need only host one piece of code on one designated URL. See the full code sample below.
- In site groups, the extension is defined in the parent site, and applies to all sites in the group.
JWS
Gigya's HTTPS post requests are a JSON body that contain a single base-64 encoded jws parameter.
The JWS is structured of three parts, separated by a period:
- Header: the algorithm and key ID
- Payload: the extension request parameters. The payload contains common parameters (see below), and different parameters per each endpoint (see the explanations for each endpoint, below)
- Signature
In the following manner:
{ "jws": "[header].[payload].[signature]" }
Use accounts.getJWTPublicKey to retrieve the public key for JWT validation.
See below for a code sample.
Verifying the JWS
Following is some pseudo-code JavaScript for Node JS used for verifying the JWS. You can use it as a basis for your own verification code:
var getPem = require('rsa-pem-from-mod-exp'); var crypto = require('crypto'); function verify(jws, n, e){ var pem = getPem(n, e); var signature = jws.split('.')[2]; var securedInput = jws.split('.', 2).join('.'); var verifier = crypto.createVerify('RSA-SHA256'); verifier.update(securedInput); return verifier.verify(pem, signature, 'base64'); }
Sample JWS
Following is a sample of a post request passing the jws parameter:
POST https://test.gigya.com/register HTTP/1.1 Content-Type: application/json; charset=utf-8 { "jws": "eyJhbGciOiJSUzI1NiIsImtpZCI6IlJEQXpSRVl5TmpCRk5USTVSak5ETURrd1JEUkJNMEZDUkRRM1FqQkNSRUpDUmpZNE9ESkZRUSJ9.eyJhcGlLZXkiOiIzX096aXd3dy0tLTQ0NDRhM3NVbXhFUUtBcTBxZ1pJZG83OUpTd0VrYm9xa2xQMkRQN2FRblFjTUFzM2VxNXd3d3ciLCJwYXJlbnRBcGlLZXkiOiIzX096aXd3dy0tLTQ0NDRhM3NVbXhFUUtBcTBxZ1pJZG83OUpTd0VrYm9xa2xQMkRQN2FRblFjTUFzM2VxNXd3d3ciLCJjYWxsSUQiOiI0YzkxMDVmMWM3NmI0NmViYjNmMDA3ZWFkZTc3ZGZjZiIsImV4dGVuc2lvblBvaW50IjoiT25CZWZvcmVBY2NvdW50c0xvZ2luIiwiZGF0YSI6e319.zYRS4Y6pjKbQOYqHUuRf_iEY7KkNS6lqPQTHOimzUHF8o5q25V1k2fcO4o8wkLt1Yh5zERvC4d6BUnIAbcrbLhHfhJfAiuT4FuDmzedjgORuVFRUGZh2dFQvpDxbpsJkbJ4aXtE4nJE3FpAFCxh5yRhI5Y0NPUlKDxpxW8omkV4VVur13I-OOcVKIn8iBFgNFQV17lwgc6S3gt5kYiFvlIQ9WfMInnt0ozj0GpFLTKn1wWkeYGNxaOYuHOx7jtaDl_4cEN2fHKPA9_awjyNQQNKMt7jL6wsmFfvIHdaCAX8Qy9zQ-TQW-XBn-pSQOopoYyq0cGVe0Yu44HhyFwrtLg" }
This translates as follows:
Header:
{ "alg": "RS256", "kid": "RDAzREYyNjBFNTI5RjNDMDkwRDRBM0FCRDQ3QjBCREJCRjY4ODJFQQ" }
Payload:
{ "apiKey": "3_Oziwww---4444a3sUmxEQKAq0qgZIdo79JSwEkboqklP2DP7aQnQcMAs3eq5wwww", "parentApiKey": "3_Oziwww---4444a3sUmxEQKAq0qgZIdo79JSwEkboqklP2DP7aQnQcMAs3eq5wwww", "callID": "4c9105f1c76b46ebb3f007eade77dfcf", "extensionPoint": "OnBeforeAccountsLogin", "data": {} }
Followed by a signature verification section.
Common Payload
The shared parameters that are always sent from Gigya when posting to the URL:
Name | Type | Description |
apiKey | string | The API key of the site for which this extension and API are run. |
callID | string | The call ID that is used throughout the flow by the Gigya APIs. The call ID is used for tracking and troubleshooting. |
extensionPoint | string | The name of the extension point. Acceptable values are: OnBeforeAccountsLogin, OnBeforeAccountsRegister, OnBeforeSetAccountInfo, OnBeforeSocialLogin. |
parentApiKey | string | If the site is part of a site group, the API key of the parent site for which this extension and API are run. |
data | JSON object | Contains the params and context JSON object, and may contain additional objects or parameters depending upon the extension point being called.
|
Processing the Response
To retrieve and handle the data from the extensions, you must register the onError event of the method you are using (Screen-Set Events, Plugin Events) and implement logic to handle any errors that are returned, i.e.,
gigya.accounts.showScreenSet({ "screenSet": "Default-RegistrationLogin", "onError": function(e) { // do something with the userFacingMessage returned from the extension // if (e.userFacingMessage == "my message") { // do something //} } }); // OR // gigya.socialize.showLoginUI({ "provider":"saml-idp1", "onError": function(e) { // do something with the userFacingMessage returned from the extension // if (e.userFacingMessage == "my message") { // do something //} } });
OnBeforeAccountsRegister
This extension point is triggered within the accounts.register API, right after Gigya runs all validation checks that are required for creating the user in the database and right before creating the user. After this point (unless an extension point returns an error indicating Gigya to fail the request), the user will be created. The newly created user may be in a 'Pending Finalization' state (e.g. if a few required fields are missing) and will then need to complete more steps in order to be fully registered.
The following flow describes the interaction between Gigya and the extension service during a user's registration flow, when the fallback policy is set to ignore all errors. Note that in this case, unless the response returned to Gigya from the extension URL specifically requests to fail the registration, Gigya will proceed with the registration as usual.
Unique Parameters Sent
These are the parameters unique to this extension type that you can expect to receive from Gigya, followed by the full structure of the post request.
Name | Type | Description | ||||||||||||||||||||||||||||||||||||||||||
apiKey | string | The apiKey of the IdP processing the login. | ||||||||||||||||||||||||||||||||||||||||||
callID | string | The callID of the response. | ||||||||||||||||||||||||||||||||||||||||||
extensionPoint | string | The extension point returning the response. | ||||||||||||||||||||||||||||||||||||||||||
data | JSON | The data object includes:
|
Payload Sample
{ "apiKey": "3_ve...tyu", "callID": "809.....169f", "extensionPoint": "OnBeforeAccountsRegister", "data": { "params": { "email": "test1@test.com", "password": "what ever password the user entered", "profile": { "firstName": "test" }, "data": { "terms": true } }, "context": { "clientIP": "1.0.0.0" } } }
Extension Response
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 { "status": "OK", }
Response indicating that the registration should be failed:
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 { "status": "FAIL", "data": { "validationErrors" : [ // Array of up to 50 items, in the following structure: { "fieldName": string of up to 200 characters containing the name of the field that had an issue "message": string of up to 2000 characters containing the custom error message the user will see in regards to this field } ] } }
For example, if the extension validation code decided the registration should be failed:
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 { "status": "FAIL", "data": { "validationErrors": [ { "fieldName": "profile.email", "message": "The email should belong to the 'gigya.com' domain" }, { "fieldName": "profile.firstName", "message": "We consider this word offensive, please use something else" }, ] } }
OnBeforeAccountsLogin
This extension point is triggered during accounts.login, right after Gigya runs the validation checks that are required for logging the user in. It is triggered in a standard login process, i.e. not in a re-authentication or link accounts scenario.
If two-factor authentication is set up on your site as part of Risk Based Authentication, and a user is asked to perform a second authentication (i.e., a TFA flow was triggered), the OnBeforeAccountsLogin extension will not be fired.
Unique Parameters Sent
These are the parameters unique to this extension type that you can expect to receive from Gigya, followed by the full structure of the post request.
Name | Type | Description | ||||||||||||
apiKey | string | The apiKey of the IdP processing the login. | ||||||||||||
callID | string | The callID of the response. | ||||||||||||
extensionPoint | string | The extension point returning the response. | ||||||||||||
data | JSON | The data object includes:
|
Payload Sample
{ "apiKey": "3_ve...yu", "callID": "806a...d0df", "extensionPoint": "OnBeforeAccountsLogin", "data": { "params": { "loginId": "test@gigya.com", "password": "the password passed by the user", "lang": "en" }, "accountInfo": { "registeredTimestamp": 1530197465, "UID": "21527d...3f6", "created": "2018-06-28T14:50:55.859Z", "createdTimestamp": 1530197455, "data": { "terms": true }, "subscriptions": {}, "preferences": {}, "isActive": true, "isRegistered": true, "isVerified": false, "lastLogin": "2018-06-28T14:52:23.594Z", "lastLoginTimestamp": 1530197543, "lastUpdated": "2018-06-28T14:51:05.291Z", "lastUpdatedTimestamp": 1530197465291, "loginProvider": "site", "oldestDataUpdated": "2018-06-28T14:50:55.859Z", "oldestDataUpdatedTimestamp": 1530197455859, "profile": { "firstName": "Name", "email": "test@gigya.com" }, "registered": "2018-06-28T14:51:05.291Z", "socialProviders": "site" }, "context": { "clientIP": "1.0.0.0" } } }
Extension Response
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 { "status": "OK", }
Response indicating that the login should be failed:
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 { "status": "FAIL", "data" : { "userFacingErrorMessage": "string of up to 2000 characters" } }
For example:
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 { "status": "FAIL", "data": { "userFacingErrorMessage": "Cannot login from outside USA" } }
OnBeforeSocialLogin
This extension point is triggered during socialize.login, right after Gigya runs the validation checks that are required for logging the user in. It is triggered in a standard login process, i.e. not in a re-authentication or link accounts scenario.
Unique Parameters Sent
These are the parameters unique to this extension type that you can expect to receive from Gigya, followed by the full structure of the post request.
Name | Type | Description | ||||||||||||||||||
apiKey | string | The apiKey of the IdP processing the login. | ||||||||||||||||||
callID | string | The callID of the response. | ||||||||||||||||||
extensionPoint | string | The extension point returning the response. | ||||||||||||||||||
data | JSON | The data object includes:
|
Payload Sample
{ "apiKey":"3_ve...yu", "callID":"806a...d0df", "extensionPoint": "OnBeforeSocialLogin", "data": { "isNewUser":false, // If (isNewUser === true) there is NO accountInfo object returned "accountInfo": { "registeredTimestamp":73827827334, "UID":"", ..., "profile": {}, "data": {}, "identities": {}, "preferences": {}, "subscriptions": {} }, "context": { "clientIp":"" }, "params": { "provider":"saml", "lang": "en" }, "providerIdentity": { "providerUid":"", "firstName":"", "lastName":"", ... "samlData": {}, "oidcData": {} } } }
Extension Response
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 { "status": "OK", }
Response indicating that the login should be failed:
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 { "status": "FAIL", "data" : { "userFacingErrorMessage": "string of up to 2000 characters" } }
For example:
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 { "status": "FAIL", "data": { "userFacingErrorMessage": "Cannot login from outside USA" } }
OnBeforeSetAccountInfo
This extension point is triggered during accounts.setAccountInfo, immediately before account data is written to the database.
Unique Parameters Sent
These are the parameters unique to this extension type that you can expect to receive from Gigya, followed by the full structure of the post request.
Name | Type | Description | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
apiKey | string | The apiKey of the IdP processing the login. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
callID | string | The callID of the response. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
extensionPoint | string | The extension point returning the response. | |||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
data | JSON | The data object includes:
|
Payload Sample
{ "apiKey": "3_v...yu", "callID": "875796577b0444b79f6ec6592bf40d30", "extensionPoint": "OnBeforeSetAccountInfo", "data": { "params": { "profile": { "firstName": "test" }, "secretAnswer": "success", }, "accountInfo": { "registeredTimestamp": 1530606852, "UID": "b1c...43", "created": "2018-07-03T08:34:10.815Z", "createdTimestamp": 1530606850, "data": {}, "subscriptions": {}, "preferences": {}, "isActive": true, "isRegistered": true, "isVerified": false, "lastLogin": "2018-07-03T09:15:31.487Z", "lastLoginTimestamp": 1530609331, "lastUpdated": "2018-07-03T09:15:33.568Z", "lastUpdatedTimestamp": 1530609333568, "loginProvider": "site", "oldestDataUpdated": "2018-07-03T08:34:10.815Z", "oldestDataUpdatedTimestamp": 1530606850815, "profile": { "email": "test1@test.com"" }, "registered": "2018-07-03T08:34:12.041Z", "socialProviders": "site" } } }
Extension Response
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 { "status": "OK", }
Response indicating that the user's profile update should be stopped:
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 { "status": "FAIL", "data": { "validationErrors" : [ // Array of up to 50 items, in the following structure: { "fieldName": string up to 200 characters - the name of the field that had an issue "message": string up to 2000 characters - the custom error message the user will see in regard with this field } } ] }
For example:
HTTP/1.1 200 OK Content-Type: application/json; charset=utf-8 { "status": "FAIL", "data": { "validationErrors": [ { "fieldName": "profile.email", "message": "email should belong to domain 'gigya.com'" }, { "fieldName": "profile.firstName", "message": "We consider this word offensive, please use something else" } ] } }
Full Code Sample
The following code sample demonstrates 3 simple validations performed using the three extension endpoints:
- OnBeforeAccountsRegister: Prevent users from registering with any domain but the "xyz" domain. Also, display a different message for a Hebrew-speaking user.
- OnBeforeAccountsLogin: Block the user from logging in if their first name is "Block" and the last name is "Me". Also, display a different message for a Hebrew-speaking user.
- OnBeforeSetAccountInfo: Prevent the user from changing their name to one that contains a negative word, in this case, "fail". Also, display a different message for a Hebrew-speaking user.
Following is a full JavaScript code sample for a node.JS application:
function handleExtensionsRequest(request, response) { var ret = {status: "OK"}; if (request.body.extensionPoint === "OnBeforeAccountsRegister") { if (!request.body.data.params.email.endsWith('@xyz.com')){ ret.status = "FAIL"; var customMessage = "Email should belong to domain 'xyz.com'"; if (request.body.data.params.lang === "he") customMessage = "אימייל צריך להיות בדומיין איקס ווי זד"; ret.data = { validationErrors: [ { fieldName: "profile.email", message: customMessage }, ] } } } else if (request.body.extensionPoint === "OnBeforeAccountsLogin") { if (request.body.data.accountInfo !== undefined && request.body.data.accountInfo.profile !== undefined && request.body.data.accountInfo.profile.firstName === "block" && request.body.data.accountInfo.profile.lastName === "me") { ret.status = "FAIL"; var customMessage = "Your account is temporarly blocked"; if (request.body.data.params.lang === "he") customMessage = "חשבונך חסום באופן זמני"; ret.data = { userFacingErrorMessage: customMessage }; } } else if (request.body.extensionPoint === "OnBeforeSetAccountInfo"){ if (request.body.data.params.profile !== undefined && request.body.data.params.profile.firstName !== undefined && request.body.data.params.profile.firstName.includes("fail")) { ret.status = "FAIL"; var customMessage = "Invalid name - contains a word with a negetive meaning"; if (request.body.data.params.lang === "he") customMessage = "שם לא חוקי - מכיל מילה עם משמעות שלילית"; ret.data = { validationErrors: [ { fieldName: "profile.firstName", message: customMessage } ] }; } } response.setHeader('Content-Type', 'application/json'); response.send(JSON.stringify(ret)); }
This is how it would appear to end users:
A user trying to register with domain "x":
Will receive a custom error:
And when registering with that domain when the 'lang' parameter is "he"(Hebrew):
Now the user tries to change their first name:
A blocked user attempts to register:
Viewing Extension Logs
A log is written every time an attempt is made to call an extension.
To view the extension logs:
- Open the Extensions page of Gigya's Console.
- Open the Logs tab.
- By default, log records from the last 7 days are displayed, sorted by the time they were recorded in descending order, up to a limit of 1,000 entries.
Log Filters
You can filter the results to locate a specific log record.
- Time Range: the time at which the call was recorded.
- Extension Point: the type of the extension that was called.
- Call ID: the unique identifier of the call.
Call Details
The tooltips in the first table column indicate which response was received from the extension:
("v" icon): The extension sent a valid response, and allowed Gigya's flow to proceed ("green light").
("x" icon): The extension sent a malformed response, or the response took too long. The flow was allowed to continue or stopped, depending on the configuration of the fallout policy.
("!" icon): The extension sent a valid response that indicated to Gigya that the flow should be stopped.
The following information is available for each call recorded in the logs:
- Timestamp: the date and time at which the call was made.
- Duration: the duration of the call.
- Extension Point: the extension type (e.g., onBeforeAccountsRegister).
- Message: the message returned from the extension service in regards to this extension call.
IP Whitelisting
You should ensure that your systems that interact with Gigya Extensions can access to the relevant IP addresses. If your system administrators explicitly white-list IPs, you should include the following to work with Extensions:
US*:
- 52.204.240.189
- 18.208.46.48
- 54.227.162.232
- 23.20.2.50
- 54.175.227.163
- 18.210.248.215
- 18.211.46.230
- 34.237.6.119
- 34.238.1.105
- 35.153.145.8
* These will be in use from September 4, 2018. Until then, the older US IPs should be used:
- 74.120.148.141
- 74.120.148.142
- 74.120.148.143
- 74.120.148.144
- 74.120.148.253
- 199.87.115.125
- 52.2.37.76
EU:
- 46.51.204.12
- 54.76.191.69
AU:
- 54.66.141.200
- 54.66.139.77
RU:
- 95.213.253.43
- 95.213.238.43
CN:
101.132.236.215
Additional Information
Associated Rest APIs include the following:
- accounts.extensions.create
- accounts.extensions.modify
- accounts.extensions.list
- accounts.extensions.get
- accounts.extensions.delete