TML-SSO
The TML SSO package is a shared library for Tomorrowland websites. It allows your client side application to easily check if a TML user is logged in, get user information or assists in adding shared functionality across applications
Compatibility
Browser-only, compatibility with mobile, and all modern browsers (+ IE11) should be supported.
Possible Extra features
- Automate login redirect process (popup, new tab, iframe, login dialog, ...)
Usage
As an npm module
Add the library to your application by adding it as an NPM dependency.
All classes are available trough import {[name]} from '@tomorrowland/sso
.
Install: npm install @tomorrowland/sso
or yarn add @tomorrowland/sso
import TMLUser from '@tomorrowland/sso'
var tmlUser = new TMLUser();
tmlUser.getUser()
.then(att => console.log(att));
As a direct <script> tag
You can easily incorporate TML user checking into your application by adding the following piece of HTML/JS.
All classes are available trough window.TML
.
HEAD
<script src="https://cdn.assets.tomorrowland.com/scripts/tml-sso-2.0.2.min.js"></script>
<script type="text/javascript">
var tmlUser = new window.TML.TMLUser();
</script>
Functionality
There are several different classes in this library, that each perform their own specialised actions.
TMLUser
This class will, by default, do the following:
- Check if a user is currently logged into the .tomorrowland.com domain (by checking the cookies) and return a Promise
- If the user is logged in, resolves the Promise with the user information
- The user information is a merged dataset of our own API and the cognito API
- Cognito is the main datasource and HAS to be available
- TML api is optional and if unavailable will not result in an error
- If the user is NOT logged in, and the session cannot be started, the promise is rejected with an error.
- If the user is logged in, resolves the Promise with the user information
- Keep the user session alive by refreshing the session once every 20 minutes starting with an initial refresh on load.
Sample usage:
const tmlUser = new TMLUser();
tmlUser.getUser()
.then(user => {
// User is logged in, store this for reuse
// Access properties like givenName:
alert(user.givenName + ' is logged in');
})
.catch(error => {
// Something went wrong, user is probably not correctly authenticated
// Redirect to login with callback to get back here
window.location = 'https://my.tomorrowland.com/login?callback=' + window.location;
});
Auto-login
An auto-login is available if the uri used to instantiate the tmlUser has the following query parameters:
-
username
: The cognito username -
refresh-token
: A valid refresh token linked to the givenusername
Configuration options
The TMLUser class has an optional options parameter. By default we will connect to the tomorrowland production site, but you might want to change this for testing purposes.
The following options are available:
-
awsUserPoolId
(string) The amazon userpool id you want to use -
awsClientId
(string) The amazon client id for the endpoint you want to use -
cookieDomain
(string) The cookie domain on which you are testing (e.x..mydomain.com
) -
allowInsecureCookies
(boolean) Indicate if you want to allow insecure cookies or not -
tmlApiBaseUrl
(string) The base url that should be used for communicating with the tomorrowland api (e.x.https://tml.amazonaws.com/Staging
) -
guestLogin
(boolean: default true) Enable/Disable guest login (this wil either show or hide the 'continue as guest' link) -
beastmode
(boolean: default true) When beastmode is enabled, only cognito will get queried for user data and not the user API
All options are optional and all non-provided options will be set to the default values.
Example:
var tmlUser = new TMLUser({
awsUserPoolId: 'someId',
awsClientId: 'someClientId',
cookieDomain: '.mydomain.com',
tmlApiBaseUrl: 'https://some.specified.emndpoint',
});
Methods
-
getUser(retry = false, allowSessionFailure = false, forceRefresh = false)
: Returns a promise that will resolve to an object with user attributes or fail if no active session was found- Will also keep the session alive in the background (if one could be established)
-
retry
: Use built in retry system to recover from API overload -
allowSessionFailure
: This will still try to fetch your user even if no valid local session is found (used for identity providers only!) -
forceRefresh
: Set to true to ALWAYS do a server request, otherwise cached user will be returned (to minimise API calls)
-
getUserPool
: Returns a promise that will always resolve with the cognito userPool object, even if no active session was found- Will also keep the session alive in the background (if one could be established)
-
getApiClient
: Returns a promise that will resolve to a client to communicate with the TML api, which will automatically add authentication headers -
getCognitoUser
: Returns a promise that will resolve to the actual cognito user -
getIdToken
: Returns a promise that will resolve to the actual cognito id token -
createDummyUser
: Create a dummy user with a given username to authenticate with cognito -
reset
: Will reset the TMLUser back to the initial state (before any other operation was performed), stopping the keep-alive -
loginPopup
: Will show a popup modal with the TML login flow. This function has one optional paramteronLogin
which is a method that will be called when a user has sucesfully logged in (receiving theuser
as a single parameter). If this parameter is nog passed, the page will reload after login. TheloginPopup
method returns a reference to the login modal so you can.hide()
it when you want. -
listenToUserChange
: Method through wich you can register a callback that is called whenever a newuser
object is fetched. ex. whengetUser()
was triggered or when a new login had occured. The callback will receive the user attributes that were just fetched as property. -
registerStatusListener
: Method to register a callback that will be called on status issues with cognito. A statuscode together with an Error will be provided. Following statuscodes are available:-
refresh-warning
: A refresh of the local token failed, but the session is still valid. -
refresh-error
: A refresh of the local token failed and the session is no longer valid!
-
-
generatePld([affiliate], [binaryEncode = false])
: Async method to generate a pld for the current user. If no user is authenticated, all fields will remain empty, except for the given affiliate -
generatePldSync(user, [affiliate], [binaryEncode = false])
: Sync method to generate a pld for the current user. If no user is authenticated, all fields will remain empty, except for the given affiliate. OPtional third parameter to enable binary encoding of the string
Available user attributes
At minimum the following properties will be available on the user object:
{
username, // String (Cognito guid)
givenName, // String
familyName, // String
company, // String
email, // String
phoneCountryCode, // String
phone, // String
dobDay, // String (Date of birth: day, zero padded, 1 - 31)
dobMonth, // String (Date of birth: month, zero padded, 1 - 12)
dobYear, // String (Date of birth: year)
gender, // String [male, female]
street, // String
buildingNumber, // String
boxNumber, // String
city, // String
zip, // String
state, // String
country, // String (ISO country code, 2 letters)
nationality, // String (ISO country code, 2 letters)
lastPrereg, // Object
first20, // Boolean?
createdAt, // String (timestamp, milliseconds)
waitingList, // Object
allowGeneralInfoChecked, // Boolean
allowPartnerInfoChecked, // Boolean
agreedTermsAndConditions, // Boolean
generalInfoCheckedAt, // String (timestamp milliseconds)
partnerInfoCheckedAt, // String (timestamp milliseconds)
termsAndConditionsAgreedAt, // String (timestamp milliseconds)
}
TopBar
This class can be used to inject a topBar into the current website, which is preconfigured to contain all necessary content.
By calling the init
method, the topbar will be injected (HTML + CSS).
This will happen as follows:
- It will look for an element with id
topBar
, remove all accociated classNames and replace them withtopBar
and replace all content with it's own content - If no element can be found, a new div with id
topBar
will be injected into the body as the first node of the tree and that node will be used as described above.
Sample usage:
const tmlUser = new TMLUser();
var topbar = new TopBar({
userIntegration: 'full',
interceptRadioNavigation: 'full',
useSocialIcons: false
});
topbar.init(tmlUser);
Methods
-
init
: Call this method with an instantiated TMLUser to inject the topBar into the document.
Configuration options
The TopBar constructor has an optional options parameter. All settings are set to general defaults, but you might want to overwite some depending on the application.
The following options are available:
const defaultConfig = {
// Do you want to open up the radio panel automatically or not?
// true: We open the radio panel on load (only on large enough displays and if not previously closed)
// false: We don't automatically open the panel under any condition
autoOpenRadio: true,
// How do you want to integrate with the user system?
// full: fetch and show user
// auto: fetch and show user, assign login modal to button for automation
// disabled: don't fetch but show signin (user name will never be shown)
// hidden: don't fetch and don't show
userIntegration: 'full',
// When using userIntegration auto, this is an optional callback method to listen to new user
userCallback: undefined,
// How do you want to treat navigation when the radio is playing?
// full: We intercept all a tags (except the ones that specified the 'preventintercept' class)
// topBar: We only intercept a tags in the topBar (except the ones that specified the 'preventintercept' class)
// disabled: We don't intercept any a tags
interceptRadioNavigation: 'full',
// Do you want to use the social media icons or not?
// true: All social media icons are shown
// false: No social media icons are shown
// [object]:
// {
// facebook: (optional) uri to use, {label, uri} array or something falsy to hide the icon. If not specified, default will be used
// instagram: (optional) uri to use, {label, uri} array or something falsy to hide the icon. If not specified, default will be used
// twitter: (optional) uri to use, {label, uri} array or something falsy to hide the icon. If not specified, default will be used
// }
useSocialIcons: true,
// How do you want to position the topbar?
// 'absolute': use absolute positioning to get it to the top (default)
// 'fixed': use fixed positioning to get it to the top (my.tomorrowland)
position: 'absolute',
// Where should we nagivate to when clicking on the user in the top right, default: `https://my.tomorrowland.com/login?callback=${window.location}`
userTarget: undefined,
// Where do you want to inject the topbar? (NOTE: ALL the contents of this node will be replaced as well as the class names!)
// If the property is falsy or the node cannot be found, a new node will be injected as the first node in the body.
// string: id of an element you want to inject the topbar in (preferably a div close to the start of the body)
// DOMNode: a reference to the DOMNode you want to inject in
targetElement: 'topBar',
// How should the radio button act?
// 'popout': When clicked a popout to start playing will be shown
// 'redirect': When clicked, the user is redirected to https://www.tomorrowland.com
// 'persistent': The popout is always visible and cannot be closed
// [function]: Function will be called when a user presses play or pause
radioToggle: 'popout'
};
All options are optional and all non-provided options will be set to the default values.
Example:
var topbar = new TopBar({
userIntegration: 'full',
interceptRadioNavigation: 'full',
useSocialIcons: {
facebook: 'http://www.testfacebook.com',
instagram: [
{ label: 'first', uri: 'https://www.firstinstagram.com' },
{ label: 'second', uri: 'https://www.secondinstagram.com' }
],
twitter: null
}
});
Modal
TODO - How to use a general modal (no specific functionality except for visualisation)
Contributing
When modifying, please avoid using async functions in this lib. We love async functions, but for older browsers, they require a bandwidth-hungry polyfill we would prefer not to include.