New Case Study:See how Anthropic automated 95% of dependency reviews with Socket.Learn More
Socket
Sign inDemoInstall
Socket

@schibsted/account-sdk-browser

Package Overview
Dependencies
Maintainers
8
Versions
95
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

@schibsted/account-sdk-browser - npm Package Compare versions

Comparing version 3.4.0 to 4.0.0

15

CHANGELOG.md
# Changelog
## v4.0.0 (2020-09-03)
## Breaking changes
* `sessionDomain` is required
* Removed `siteSpecificLogout` param- there is no option to disable site specific logout
* `hasSession` does not accept `autologin` param anymore
* Drop `hasProduct` and `hasSubscription` functions- replaced by `hasAccess`
## New features
* Add 2FA Support- new `acrValues`: `sms`, `otp`, `password`
* `logSettings` function
## Changes
* babel has been updated to `^7.11`
## v3.4.0 (2020-04-28)

@@ -4,0 +19,0 @@

1

index.d.ts

@@ -9,4 +9,5 @@ export class Identity {

hasSession(): Promise<any>
logSettings(): void
_enableSessionCaching: boolean;
_itpMode: boolean;
}

24

package.json
{
"name": "@schibsted/account-sdk-browser",
"version": "3.4.0",
"version": "4.0.0",
"description": "Schibsted account SDK for browsers",

@@ -20,20 +20,20 @@ "main": "index.js",

"dependencies": {
"fetch-jsonp": "^1.1.3",
"tiny-emitter": "^2.1.0"
},
"devDependencies": {
"babel-core": "^6.26.3",
"babel-loader": "^7.1.4",
"babel-preset-env": "^1.7.0",
"@babel/core": "^7.11.4",
"@babel/preset-env": "^7.11.0",
"babel-loader": "^8.1.0",
"codecov": "^3.6.5",
"core-js": "^3.6.5",
"docdash": "git+https://github.com/torarvid/docdash.git#v0.5.0",
"eslint": "^6.8.0",
"eslint-plugin-import": "^2.20.2",
"jest": "^23.6.0",
"jest": "^26.4.2",
"jest-junit": "^10.0.0",
"jsdoc": "^3.6.4",
"jsdoc": "^3.6.5",
"node-fetch": "^2.6.0",
"regenerator-runtime": "^0.13.5",
"webpack": "^4.43.0",
"webpack-cli": "^3.3.11",
"regenerator-runtime": "^0.13.7",
"webpack": "^4.44.1",
"webpack-cli": "^3.3.12",
"whatwg-url": "^8.0.0"

@@ -48,4 +48,6 @@ },

[
"env",
"@babel/preset-env",
{
"useBuiltIns": "usage",
"corejs": 3,
"targets": {

@@ -52,0 +54,0 @@ "browsers": [

@@ -153,17 +153,4 @@ [![logo](https://www.schibsted.com/Global/LogoTypes/Logos%202014/SMG_Small_2014_RGB.png)](https://github.com/schibsted/account-sdk-browser)

There are two ways to deal with this in Safari:
1. You can continue with 3rd party requests, but this requires an iframe on that 3rd party domain
and it also requires user input in said iframe
2. You can re-design the system to not use 3rd party requests anymore
We've tried to support both. For the 1st strategy, simply continue using the SDK like before. Be
sure to call `Identity.login` when authenticating, and `Identity.hasSession` when coming back to
your site from Schibsted account. This should pop up our so-called "ITP Dialog". This is the iframe
mentioned in point 1 above, and clicking the Continue button in that frame will ensure the
`hasSession` call running inside the iframe is successful. The benefit of this strategy is it
requires very little work from you. The drawback is that every time you come back from
authentication, the user will have to see this "ITP dialog".
So to work with strategy 2, we have re-designed our platform and introduced what we call the
To ensure consistent user sessions despite these restrictions, we have re-designed our platform and
introduced what we call the
session-service. If your site lives on site.example, you should assign a sub-domain for use with the

@@ -189,5 +176,3 @@ session-service (we propose id.site.example for production and id-pre.site.example for staging —

1 and 2 requires communication with us, and 3 is done by you at a time of your choosing. The benefit
of this strategy is that we should never need to show any dialog or popup to the user, so it reduces
friction. The drawback is the work mentioned above.
1 and 2 requires [communication with us](mailto:schibstedaccount@schibsted.com), and 3 is done by you at a time of your choosing.

@@ -312,2 +297,4 @@ <a name="example-project"></a>

the code on their phone as an SMS
* Multifactor authentication: first client indicates which methods should be preferred, later these
will be included (if fulfilled) in `AMR` claim of IDToken

@@ -318,4 +305,10 @@ IMPORTANT: Passwordless using SMS is still in BETA. It's only recommended to use it for testing and

The default is username & password. If you wish to use one of the passwordless login methods, the
`login()` function takes an optional parameter called `acrValues` (yeah, it's an OAuth specific
name). Please set this parameter to either `otp-email` or `otp-sms`.
`login()` function takes an optional parameter called `acrValues` (Authentication Context Class Reference).
The `acrValues` parameter with multifactor authentication can take following values:
- `otp-email` - passwordless authentication using code sent to registered email
- `otp-sms` - passwordless authentication using code sent to registered phone number
- `password` - force password authentication (even if user is already logged in)
- `otp` - authentication using registered one time code generator (https://tools.ietf.org/html/rfc6238)
- `sms` - authentication using SMS code sent to phone number
- `password otp sms` - those authentication methods might be combined

@@ -397,35 +390,2 @@ The classic way to authenticate a user, is to send them from your site to the Schibsted account

### Legacy methods
* [Monetization#hasProduct](https://schibsted.github.io/account-sdk-browser/Monetization.html#hasProduct)
for checking if the user has access to a particular product
* [Monetization#hasSubscription](https://schibsted.github.io/account-sdk-browser/Monetization.html#hasSubscription)
for checking if the user has access to a particular subscription
These two functions require a parameter `sp_id` that is obtained from
[Identity#getSpId](https://schibsted.github.io/account-sdk-browser/Identity.html#getSpId)
asynchronously.
#### Example
```javascript
import { Monetization } from '@schibsted/account-sdk-browser'
const monetization = new Monetization({
clientId: '56e9a5d1eee0000000000000',
redirectUri: 'https://awesomenews.site', // ensure it's listed in selfservice
env: 'PRE', // Schibsted account env. A url or a special key: 'PRE', 'PRO' or 'PRO_NO'
})
try {
// Check if the user has access to a a particular product
// You need the sp_id parameter that is obtained from an Identity instance
const sp_id = await identity.getSpId()
const data = await monetization.hasProduct(productId, sp_id)
alert(`User has access to ${productId}? ${data.result}`)
} catch (err) {
alert(`Could not query if the user has access to ${productId} because ${err}`)
}
```
<a name="payment"></a>

@@ -432,0 +392,0 @@

@@ -1,27 +0,7 @@

import { URLSearchParams, URL } from 'url';
import { URL } from 'url';
import { urlMapper } from '../url';
import { cloneDefined } from '../object';
import { Fixtures } from '../../__tests__/utils';
import SDKError from '../../src/SDKError';
const goFn = () => jest.fn().mockImplementation(async ({ pathname, data = {} }) => {
const search = new URLSearchParams(data);
if (pathname.startsWith('/hasProduct/')) {
if (pathname.endsWith('/existing')) {
return Fixtures.spidProduct;
} else if (pathname.endsWith('no-session-cookie')) {
throw new SDKError('Session cookie (schacc-session) missing', { code: 400 });
} else if (pathname.endsWith('no-session')) {
throw new SDKError('No session', { code: 401 });
}
}
if (pathname.startsWith('/hasSubscription/')) {
if (pathname.endsWith('/existing')) {
return Fixtures.spidProduct;
} else if (pathname.endsWith('no-session-cookie')) {
throw new SDKError('Session cookie (schacc-session) missing', { code: 400 });
} else if (pathname.endsWith('no-session')) {
throw new SDKError('No session', { code: 401 });
}
}
const goFn = () => jest.fn().mockImplementation(async ({ pathname }) => {
if (pathname.startsWith('/hasAccess/')) {

@@ -36,32 +16,2 @@ if (pathname.endsWith('/existing')) {

}
if (pathname === 'ajax/hasproduct.js') {
const productId = search.get('product_id');
const spId = search.get('sp_id');
switch (productId) {
case 'existing':
return Fixtures.spidProduct;
case 'existing_no_expires':
return Fixtures.spidProductNoExpires;
case 'existing_for_john':
if (spId === 'john') {
return Fixtures.spidProduct;
}
}
return Fixtures.spidProductMissing;
}
if (pathname === 'ajax/hassubscription.js') {
const subscriptionId = search.get('product_id');
const spId = search.get('sp_id');
switch (subscriptionId) {
case 'existing':
return Fixtures.spidProduct;
case 'existing_no_expires':
return Fixtures.spidProductNoExpires;
case 'existing_for_john':
if (spId === 'john') {
return Fixtures.spidProduct;
}
}
return Fixtures.spidProductMissing;
}
throw new Error(`Unimplemented mock response for url: '${pathname}'`);

@@ -68,0 +18,0 @@ });

@@ -31,8 +31,2 @@ /* Copyright 2018 Schibsted Products & Technology AS. Licensed under the terms of the MIT license.

* @prop {string} ENDPOINTS.SPiD.PRO_NO - Production environment Norway
* @prop {object} ENDPOINTS.HAS_SESSION - Endpoints to check whether a user has a valid session
* @prop {string} ENDPOINTS.HAS_SESSION.LOCAL - Local endpoint (for Identity team)
* @prop {string} ENDPOINTS.HAS_SESSION.DEV - Dev environment (for Identity team)
* @prop {string} ENDPOINTS.HAS_SESSION.PRE - Staging environment
* @prop {string} ENDPOINTS.HAS_SESSION.PRO - Production environment Sweden
* @prop {string} ENDPOINTS.HAS_SESSION.PRO_NO - Production environment Norway
* @prop {object} ENDPOINTS.BFF - Endpoints used with new GDPR-compliant web flows

@@ -50,4 +44,2 @@ * @prop {string} ENDPOINTS.BFF.LOCAL - Local endpoint (for Identity team)

* @prop {string} ENDPOINTS.SESSION_SERVICE.PRO_NO - Production environment Norway
* @prop {object} JSONP
* @prop {Number} JSONP.TIMEOUT=7000 - Timeout in milliseconds
*/

@@ -63,9 +55,2 @@ const config = {

},
HAS_SESSION: {
LOCAL: 'http://session.id.localhost',
DEV: 'https://session.identity-dev.schibsted.com',
PRE: 'https://session.identity-pre.schibsted.com',
PRO: 'https://session.login.schibsted.com',
PRO_NO: 'https://session.payment.schibsted.no'
},
BFF: {

@@ -86,3 +71,2 @@ LOCAL: 'http://id.localhost/authn/',

},
JSONP: { TIMEOUT: 7000 }, // ms
NAMESPACE: {

@@ -95,7 +79,6 @@ LOCAL: 'id.localhost',

}
}
};
export default config;
export const ENDPOINTS = config.ENDPOINTS;
export const JSONP = config.JSONP;
export const NAMESPACE = config.NAMESPACE;

@@ -12,9 +12,8 @@ /* Copyright 2018 Schibsted Products & Technology AS. Licensed under the terms of the MIT license.

import EventEmitter from 'tiny-emitter';
import JSONPClient from './JSONPClient';
import Cache from './cache';
import * as popup from './popup';
import ItpModal from './ItpModal';
import RESTClient from './RESTClient';
import SDKError from './SDKError';
import * as spidTalk from './spidTalk';
import { version } from '../package.json';

@@ -74,19 +73,5 @@ /**

const HAS_SESSION_CACHE_KEY = 'hasSession-cache';
const LOGIN_IN_PROGRESS_KEY = 'loginInProgress-cache';
const globalWindow = () => window;
/**
* Get type and value of something
* @private
* @param {string} thing
* @returns {Array} Tuple of [type, value]
*/
function inspect(thing) {
if (thing === null) {
return [typeof thing, `${thing}`];
}
return [thing.constructor.name, thing.valueOf()];
}
/**
* Provides Identity functionalty to a web page

@@ -98,6 +83,5 @@ */

* @param {string} options.clientId - Example: "1234567890abcdef12345678"
* @param {string} options.sessionDomain - Example: "https://id.site.com"
* @param {string} [options.redirectUri] - Example: "https://site.com"
* @param {string} [options.sessionDomain] - Example: "https://id.site.com"
* @param {string} [options.env='PRE'] - Schibsted account environment: `PRE`, `PRO` or `PRO_NO`
* @param {boolean} [options.siteSpecificLogout=true] - Whether site-specific logout should be used
* @param {function} [options.log] - A function that receives debug log information. If not set,

@@ -107,3 +91,3 @@ * no logging will be done

*/
constructor({ clientId, redirectUri, sessionDomain, env = 'PRE', siteSpecificLogout = true, log, window = globalWindow() }) {
constructor({ clientId, redirectUri, sessionDomain, env = 'PRE', log, window = globalWindow() }) {
super();

@@ -113,2 +97,3 @@ assert(isNonEmptyString(clientId), 'clientId parameter is required');

assert(!redirectUri || isUrl(redirectUri), 'redirectUri parameter is invalid');
assert(sessionDomain && isUrl(sessionDomain), 'sessionDomain parameter is not a valid URL');

@@ -122,24 +107,15 @@ spidTalk.emulate(window);

this.env = env;
this.siteSpecificLogout = siteSpecificLogout;
this.log = log;
this._sessionDomain = sessionDomain;
if (sessionDomain) {
assert(isUrl(sessionDomain), 'sessionDomain parameter is not a valid URL');
this._setSessionServiceUrl(sessionDomain);
}
// Internal hack: set to false to always refresh from hassession
this._enableSessionCaching = true;
// Internal hack: set to true if the SDK is being used inside the ITP iframe to
// avoid using the hasSession service and to prevent infinite iframe recursion
this._itpMode = false;
// Old session
this._session = {};
this._setSessionServiceUrl(sessionDomain);
this._setSpidServerUrl(env);
this._setBffServerUrl(env);
this._setOauthServerUrl(env);
this._setHasSessionServerUrl(env);
this._setGlobalSessionServiceUrl(env);

@@ -156,3 +132,3 @@ }

assert(isStr(url), `url parameter is invalid: ${url}`);
this._spid = new JSONPClient({
this._spid = new RESTClient({
serverUrl: urlMapper(url, ENDPOINTS.SPiD),

@@ -206,3 +182,3 @@ log: this.log,

log: this.log,
defaultParams: { client_sdrn, redirect_uri: this.redirectUri },
defaultParams: { client_sdrn, redirect_uri: this.redirectUri, sdk_version: version },
});

@@ -223,3 +199,3 @@ }

log: this.log,
defaultParams: { client_sdrn },
defaultParams: { client_sdrn, sdk_version: version },
});

@@ -229,17 +205,2 @@ }

/**
* Set HasSession server URL - real URL or 'PRE' style key
* @private
* @param {string} url
* @returns {void}
*/
_setHasSessionServerUrl(url) {
assert(isStr(url), `url parameter is invalid: ${url}`);
this._hasSession = new JSONPClient({
serverUrl: urlMapper(url, ENDPOINTS.HAS_SESSION),
log: this.log,
defaultParams: { client_id: this.clientId, redirect_uri: this.redirectUri },
});
}
/**
* Emits the relevant events based on the previous and new reply from hassession

@@ -413,16 +374,22 @@ * @private

/**
* Check if we need to use the ITP workaround for Safari versions >= 12
* @private
* @returns {boolean}
* Log used settings and version
* @throws {SDKError} - If log method is not provided
* @return {void}
*/
_itpModalRequired() {
if (!document.requestStorageAccess || this._sessionService) {
return false;
logSettings() {
if (!this.log && !window.console) {
throw new SDKError('You have to provide log method in constructor');
}
const safariVersion = navigator.userAgent.match(/Version\/(\d+)\./);
if (!safariVersion || safariVersion.length < 2) {
return false;
const log = this.log || console.log;
const settings = {
clientId: this.clientId,
redirectUri: this.redirectUri,
env: this.env,
sessionDomain: this._sessionDomain,
sdkVersion: version
}
return parseInt(safariVersion[1], 10) >= 12;
log(`Schibsted account SDK for browsers settings: \n${JSON.stringify(settings, null, 2)}`);
}

@@ -433,7 +400,3 @@

* @description When we send a request to this endpoint, cookies sent along with the request
* determines the status of the user. If the user is not currently logged in, but has a cookie
* with the "Remember me" flag switched on, calling this function will attempt to automatically
* perform a login on the user
* @param {boolean} [autologin=true] - Set this to `false` if you do **not** want the auto-login
* to happen
* determines the status of the user.
* @throws {SDKError} - If the call to the hasSession service fails in any way (this will happen

@@ -451,10 +414,6 @@ * if, say, the user is not logged in)

*/
hasSession(autologin = true) {
hasSession() {
if (this._hasSessionInProgress) {
return this._hasSessionInProgress;
}
if (typeof autologin !== 'boolean') {
const [type, value] = inspect(autologin);
return Promise.reject(new SDKError(`Parameter 'autologin' must be boolean, was: "${type}:${value}"`));
}
const _postProcess = (sessionData) => {

@@ -478,41 +437,12 @@ if (sessionData.error) {

let sessionData = null;
if (this._sessionService) {
try {
sessionData = await this._sessionService.get('/session');
} catch (err) {
if (this.siteSpecificLogout) {
if (err && err.code === 400 && this._enableSessionCaching) {
const expiresIn = 1000 * (err.expiresIn || 300);
this.cache.set(HAS_SESSION_CACHE_KEY, { error: err }, expiresIn);
}
// Don't fallback to other sources for user session lookup
throw err;
}
// The session-service returns 400 if no session-cookie is sent in the
// request. This will be the case if the user hasn't logged in since the
// site switched to using the session-service. If the request contains a
// session-cookie but no session is found (return code will be 404), then we
// *should* throw an exception and *not* fall through to spid-hassession
if (err.code !== 400) {
throw err;
}
try {
sessionData = await this._sessionService.get('/session');
} catch (err) {
if (err && err.code === 400 && this._enableSessionCaching) {
const expiresIn = 1000 * (err.expiresIn || 300);
this.cache.set(HAS_SESSION_CACHE_KEY, { error: err }, expiresIn);
}
throw err;
}
const autoLoginConverted = autologin ? 1 : 0;
if (!sessionData && !this._itpMode) {
sessionData = await this._hasSession.get('rpc/hasSession.js', { autologin: autoLoginConverted });
}
if (this._itpMode || (sessionData && isObject(sessionData.error) && sessionData.error.type === 'LoginException')) {
sessionData = await this._spid.get('ajax/hasSession.js', { autologin: autoLoginConverted });
}
const shouldShowItpModal = this._itpModalRequired() && !this._itpMode && sessionData && isObject(sessionData.error)
&& sessionData.error.type === 'UserException' && this.cache.get(LOGIN_IN_PROGRESS_KEY) !== null;
if (shouldShowItpModal) {
this.cache.delete(LOGIN_IN_PROGRESS_KEY);
const modal = new ItpModal(this._spid, this.clientId, this.redirectUri, this.env);
sessionData = await modal.show()
}
if (sessionData && this._enableSessionCaching) {

@@ -680,4 +610,10 @@ const expiresIn = 1000 * (sessionData.expiresIn || 300);

* @param {string} [options.acrValues] - Authentication Context Class Reference Values. If
* omitted, the user will be asked to authenticate using username+password. 'otp-email' means
* one time password using email. 'otp-sms' means one time password using sms
* omitted, the user will be asked to authenticate using username+password.
* For 2FA (Two-Factor Authentication) possible values are `sms`, `otp` (one time password) and
* `password` (will force password confirmation, even if user is already logged in). Those values might
* be mixed as space-separated string. To make sure that user has authenticated with 2FA you need
* to verify AMR (Authentication Methods References) claim in ID token.
* Might also be used to ensure additional acr (sms, otp) for already logged in users.
* Supported values are also 'otp-email' means one time password using email, and 'otp-sms' means
* one time password using sms.
* @param {string} options.state - An opaque value used by the client to maintain state between

@@ -695,4 +631,2 @@ * the request and callback. It's also recommended to prevent CSRF

* @param {boolean} [options.preferPopup=false] - Should we try to open a popup window?
* @param {boolean} [options.newFlow=true] - Should we try the new GDPR-safe flow or the
* legacy/stable SPiD flow?
* @param {string} [options.loginHint=''] - user email or UUID hint

@@ -717,3 +651,2 @@ * @param {string} [options.tag=''] - Pulse tag

preferPopup = false,
newFlow = true,
loginHint = '',

@@ -728,7 +661,5 @@ tag = '',

this.cache.delete(HAS_SESSION_CACHE_KEY);
const url = this.loginUrl({ state, acrValues, scope, redirectUri, newFlow, loginHint, tag,
const url = this.loginUrl({ state, acrValues, scope, redirectUri, loginHint, tag,
teaser, maxAge, locale, oneStepLogin });
this.showItpModalUponReturning();
if (preferPopup) {

@@ -766,8 +697,5 @@ this.popup =

* username+password. If set to `'otp-email'`, then passwordless login using email is used. If
* `'otp-sms'`, then passwordless login using sms is used. Please note that this parameter has
* no effect if `newFlow` is false
* `'otp-sms'`, then passwordless login using sms is used.
* @param {string} [options.scope='openid']
* @param {string} [options.redirectUri=this.redirectUri]
* @param {boolean} [options.newFlow=true] - Should we try the new flow or the old Schibsted account
* login? If this parameter is set to false, the `acrValues` parameter doesn't have any effect
* @param {string} [options.loginHint=''] - user email or UUID hint

@@ -791,3 +719,2 @@ * @param {string} [options.tag=''] - Pulse tag

redirectUri = this.redirectUri,
newFlow = true,
loginHint = '',

@@ -806,9 +733,9 @@ tag = '',

redirectUri = arguments[3] || redirectUri;
newFlow = typeof arguments[4] === 'boolean' ? arguments[4] : newFlow;
loginHint = arguments[5] || loginHint;
tag = arguments[6] || tag;
teaser = arguments[7] || teaser;
maxAge = isNaN(arguments[8]) ? maxAge : arguments[8];
loginHint = arguments[4] || loginHint;
tag = arguments[5] || tag;
teaser = arguments[6] || teaser;
maxAge = isNaN(arguments[7]) ? maxAge : arguments[7];
}
assert(!acrValues || isStrIn(acrValues, ['', 'otp-email', 'otp-sms'], true),
const isValidAcrValue = (acrValue) => isStrIn(acrValue, ['password', 'otp', 'sms'], true);
assert(!acrValues || isStrIn(acrValues, ['', 'otp-email', 'otp-sms'], true) || acrValues.split(' ').every(isValidAcrValue),
`The acrValues parameter is not acceptable: ${acrValues}`);

@@ -820,32 +747,16 @@ assert(isUrl(redirectUri),

if (newFlow) {
return this._oauthService.makeUrl('oauth/authorize', {
response_type: 'code',
'new-flow': true,
redirect_uri: redirectUri,
scope,
state,
acr_values: acrValues,
login_hint: loginHint,
tag,
teaser,
max_age: maxAge,
locale,
one_step_login: oneStepLogin || '',
prompt: this.siteSpecificLogout ? 'select_account' : ''
});
} else {
// acrValues do not work with the old flows
return this._spid.makeUrl('flow/login', {
response_type: 'code',
redirect_uri: redirectUri,
scope,
state,
email: loginHint,
tag,
teaser,
locale
});
}
return this._oauthService.makeUrl('oauth/authorize', {
response_type: 'code',
redirect_uri: redirectUri,
scope,
state,
acr_values: acrValues,
login_hint: loginHint,
tag,
teaser,
max_age: maxAge,
locale,
one_step_login: oneStepLogin || '',
prompt: !acrValues ? 'select_account' : ''
});
}

@@ -861,9 +772,5 @@

const params = { redirect_uri: redirectUri };
if (this._sessionService && this.siteSpecificLogout) {
return this._sessionService.makeUrl('logout', params);
}
return this._spid.makeUrl('logout', Object.assign({ response_type: 'code' }, params));
return this._sessionService.makeUrl('logout', params);
}
/**

@@ -894,75 +801,2 @@ * The account summary page url

/**
* Url to render either signup or login
* @see https://techdocs.spid.no/flows/auth-flow/
* @param {string} [redirectUri=this.redirectUri]
* @return {string} - the url to the authentication page
*/
authFlowUrl(redirectUri = this.redirectUri) {
assert(isUrl(redirectUri), `authFlowUrl(): redirectUri is invalid`);
return this._spid.makeUrl('flow/auth', {
response_type: 'code',
redirect_uri: redirectUri
});
}
/**
* Url to render a signup view and let the user login with credentials
* @see https://techdocs.spid.no/flows/auth-flow/
* @param {string} [redirectUri=this.redirectUri]
* @return {string} - the url to the signup page
*/
signupFlowUrl(redirectUri = this.redirectUri) {
assert(isUrl(redirectUri), `signupFlowUrl(): redirectUri is invalid`);
return this._spid.makeUrl('flow/signup', {
response_type: 'code',
redirect_uri: redirectUri
});
}
/**
* To render a signin view and let the user login without credentials
* @see https://techdocs.spid.no/flows/auth-flow/
* @param {string} [redirectUri=this.redirectUri]
* @return {string} - the url to the signin page
*/
signinFlowUrl(redirectUri = this.redirectUri) {
assert(isUrl(redirectUri), `signinFlowUrl(): redirectUri is invalid`);
return this._spid.makeUrl('flow/signin', {
response_type: 'code',
redirect_uri: redirectUri
});
}
/**
* Call this method immediately before sending a user to a Schibsted account flow if you
* want to enable showing of the ITP modal upon returning to your site. You should
* send the user to a Schibsted account flow immediately after calling this method without
* invoking hasSession() again.
*
* Calling this is not required if you send the user to Schibsted account via the login()
* method.
*
* @return {void}
*/
showItpModalUponReturning() {
// for safari, remember that we've got a login in progress so we can
// work around some ITP issues when we come back from Schibsted Account
if (this._itpModalRequired()) {
this.cache.set(LOGIN_IN_PROGRESS_KEY, {}, 1000 * 60 * 15);
}
}
/**
* When returning after performing a flow, this method can be called prior to calling
* hasSession() if you're certain that the user did not successfully log in. This will
* prevent the ITP modal from showing up erroneously. If you're unsure, don't call this
* method.
*
* @return {void}
*/
suppressItpModal() {
this.cache.delete(LOGIN_IN_PROGRESS_KEY);
}
/**
* Function responsible for loading and displaying simplified login widget. How often

@@ -969,0 +803,0 @@ * widget will be display is up to you. Preferred way would be to show it once per user,

@@ -11,3 +11,2 @@ /* Copyright 2018 Schibsted Products & Technology AS. Licensed under the terms of the MIT license.

import EventEmitter from 'tiny-emitter';
import JSONPClient from './JSONPClient';
import RESTClient from './RESTClient';

@@ -17,5 +16,4 @@ import Cache from './cache';

import SDKError from './SDKError';
import { version } from '../package.json';
const DEFAULT_CACHE_NO_ACCESS = 10; // 10 seconds
const DEFAULT_CACHE_HAS_ACCESS = 1 * 60 * 60; // 1 hour
const globalWindow = () => window;

@@ -61,3 +59,3 @@

assert(isStr(url), `url parameter is invalid: ${url}`);
this._spid = new JSONPClient({
this._spid = new RESTClient({
serverUrl: urlMapper(url, ENDPOINTS.SPiD),

@@ -80,3 +78,3 @@ defaultParams: { client_id: this.clientId, redirect_uri: this.redirectUri },

log: this.log,
defaultParams: { client_sdrn, redirect_uri: this.redirectUri },
defaultParams: { client_sdrn, redirect_uri: this.redirectUri, sdk_version: version },
});

@@ -86,94 +84,2 @@ }

/**
* Checks if the user has access to a particular product
* @param {string} productId
* @param {string} spId - The spId that was obtained from {@link Identity#getSpId}
* @throws {SDKError} - If a network call fails in any way (this will happen if, say, the user
* is not logged in)
* @returns {Object|null} The data object returned from Schibsted account (or `null` if the user
* doesn't have access to the given product)
*/
async hasProduct(productId, spId) {
const cacheKey = `prd_${productId}_${spId}`;
let data = this.cache.get(cacheKey);
const shouldCache = !data;
if (!data && this._sessionService) {
try {
data = await this._sessionService.get(`/hasProduct/${productId}`);
} catch (err) {
// The session-service returns 400 if no session-cookie is sent in the request. This
// will be the case if the user hasn't logged in since the site switched to using
// the session-service. If the request contains a session-cookie but an error is
// still thrown, then we *should* throw an exception and *not* fall through to
// spid
if (err.code !== 400) {
throw err;
}
data = null;
}
}
if (!data) {
const params = { product_id: productId }
if (spId) {
params.sp_id = spId;
}
data = await this._spid.get('ajax/hasproduct.js', params);
}
if (shouldCache) {
const expiresSeconds = data.result ? DEFAULT_CACHE_HAS_ACCESS : DEFAULT_CACHE_NO_ACCESS;
this.cache.set(cacheKey, data, expiresSeconds * 1000);
}
if (!data.result) {
return null;
}
this.emit('hasProduct', { productId, data });
return data;
}
/**
* Checks if the user has access to a particular subscription
* @param {string} subscriptionId
* @param {string} spId - The spId that was obtained from {@link Identity#getSpId}
* @throws {SDKError} - If a network call fails in any way (this will happen if, say, the user
* is not logged in)
* @returns {Object|null} The data object returned from Schibsted account (or `null` if the user
* doesn't have access to the given subscription)
*/
async hasSubscription(subscriptionId, spId) {
const cacheKey = `sub_${subscriptionId}_${spId}`;
let data = this.cache.get(cacheKey);
const shouldCache = !data;
if (!data && this._sessionService) {
try {
data = await this._sessionService.get(`/hasSubscription/${subscriptionId}`);
} catch (err) {
// The session-service returns 400 if no session-cookie is sent in the request. This
// will be the case if the user hasn't logged in since the site switched to using
// the session-service. If the request contains a session-cookie but an error is
// still thrown, then we *should* throw an exception and *not* fall through to
// spid
if (err.code !== 400) {
throw err;
}
data = null;
}
}
if (!data) {
const params = { product_id: subscriptionId }
if (spId) {
params.sp_id = spId;
}
data = await this._spid.get('ajax/hassubscription.js', params);
}
if (shouldCache) {
const expiresSeconds = data.result ? DEFAULT_CACHE_HAS_ACCESS : DEFAULT_CACHE_NO_ACCESS;
this.cache.set(cacheKey, data, expiresSeconds * 1000);
}
if (!data.result) {
return null;
}
this.emit('hasSubscription', { subscriptionId, data });
return data;
}
/**
* Checks if the user has access to a set of products or features.

@@ -180,0 +86,0 @@ * @param {array} productIds - which products/features to check

@@ -10,3 +10,2 @@ /* Copyright 2018 Schibsted Products & Technology AS. Licensed under the terms of the MIT license.

import { ENDPOINTS } from './config';
import JSONPClient from './JSONPClient';
import * as popup from './popup';

@@ -50,3 +49,3 @@ import RESTClient from './RESTClient';

assert(isStr(url), `url parameter is invalid: ${url}`);
this._spid = new JSONPClient({
this._spid = new RESTClient({
serverUrl: urlMapper(url, ENDPOINTS.SPiD),

@@ -53,0 +52,0 @@ defaultParams: { client_id: this.clientId, redirect_uri: this.redirectUri },

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

Sorry, the diff of this file is too big to display

Sorry, the diff of this file is not supported yet

SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc