| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285 |
- /*!
- * Copyright (c) 2015-2016, Okta, Inc. and/or its affiliates. All rights reserved.
- * The Okta software accompanied by this notice is provided pursuant to the Apache License, Version 2.0 (the "License.")
- *
- * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0.
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
- * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- *
- * See the License for the specific language governing permissions and limitations under the License.
- */
- /* eslint-disable complexity */
- /* eslint-disable max-statements */
-
- require('./vendor/polyfills');
-
- var Q = require('q');
- var oauthUtil = require('./oauthUtil');
- var util = require('./util');
- var tx = require('./tx');
- var session = require('./session');
- var cookies = require('./cookies');
- var token = require('./token');
- var AuthSdkError = require('./errors/AuthSdkError');
- var config = require('./config');
- var TokenManager = require('./TokenManager');
- var http = require('./http');
-
- function OktaAuthBuilder(args) {
- var sdk = this;
-
- if (!args) {
- throw new AuthSdkError('No arguments passed to constructor. ' +
- 'Required usage: new OktaAuth(args)');
- }
-
- if (!args.url) {
- throw new AuthSdkError('No url passed to constructor. ' +
- 'Required usage: new OktaAuth({url: "https://sample.okta.com"})');
- }
-
- if (args.url.indexOf('-admin.') !== -1) {
- throw new AuthSdkError('URL passed to constructor contains "-admin" in subdomain. ' +
- 'Required usage: new OktaAuth({url: "https://dev-12345.okta.com})');
- }
-
- this.options = {
- url: util.removeTrailingSlash(args.url),
- clientId: args.clientId,
- issuer: util.removeTrailingSlash(args.issuer),
- authorizeUrl: util.removeTrailingSlash(args.authorizeUrl),
- userinfoUrl: util.removeTrailingSlash(args.userinfoUrl),
- redirectUri: args.redirectUri,
- ajaxRequest: args.ajaxRequest,
- transformErrorXHR: args.transformErrorXHR,
- headers: args.headers
- };
-
- this.userAgent = 'okta-auth-js-' + config.SDK_VERSION;
-
- // Digital clocks will drift over time, so the server
- // can misalign with the time reported by the browser.
- // The maxClockSkew allows relaxing the time-based
- // validation of tokens (in seconds, not milliseconds).
- // It currently defaults to 300, because 5 min is the
- // default maximum tolerance allowed by Kerberos.
- // (https://technet.microsoft.com/en-us/library/cc976357.aspx)
- if (!args.maxClockSkew && args.maxClockSkew !== 0) {
- this.options.maxClockSkew = config.DEFAULT_MAX_CLOCK_SKEW;
- } else {
- this.options.maxClockSkew = args.maxClockSkew;
- }
-
- sdk.session = {
- close: util.bind(session.closeSession, null, sdk),
- exists: util.bind(session.sessionExists, null, sdk),
- get: util.bind(session.getSession, null, sdk),
- refresh: util.bind(session.refreshSession, null, sdk),
- setCookieAndRedirect: util.bind(session.setCookieAndRedirect, null, sdk)
- };
-
- sdk.tx = {
- status: util.bind(tx.transactionStatus, null, sdk),
- resume: util.bind(tx.resumeTransaction, null, sdk),
- exists: util.bind(tx.transactionExists, null, sdk)
- };
-
- // This is exposed so we can mock document.cookie in our tests
- sdk.tx.exists._getCookie = function(name) {
- return cookies.getCookie(name);
- };
-
- sdk.idToken = {
- authorize: util.deprecateWrap('Use token.getWithoutPrompt, token.getWithPopup, or token.getWithRedirect ' +
- 'instead of idToken.authorize.', util.bind(token.getToken, null, sdk)),
- verify: util.deprecateWrap('Use token.verify instead of idToken.verify', util.bind(token.verifyIdToken, null, sdk)),
- refresh: util.deprecateWrap('Use token.refresh instead of idToken.refresh',
- util.bind(token.refreshIdToken, null, sdk)),
- decode: util.deprecateWrap('Use token.decode instead of idToken.decode', token.decodeToken)
- };
-
- // This is exposed so we can mock window.location.href in our tests
- sdk.idToken.authorize._getLocationHref = function() {
- return window.location.href;
- };
-
- sdk.token = {
- getWithoutPrompt: util.bind(token.getWithoutPrompt, null, sdk),
- getWithPopup: util.bind(token.getWithPopup, null, sdk),
- getWithRedirect: util.bind(token.getWithRedirect, null, sdk),
- parseFromUrl: util.bind(token.parseFromUrl, null, sdk),
- decode: token.decodeToken,
- refresh: util.bind(token.refreshToken, null, sdk),
- getUserInfo: util.bind(token.getUserInfo, null, sdk),
- verify: util.bind(token.verifyToken, null, sdk)
- };
-
- // This is exposed so we can set window.location in our tests
- sdk.token.getWithRedirect._setLocation = function(url) {
- window.location = url;
- };
-
- // This is exposed so we can mock getting window.history in our tests
- sdk.token.parseFromUrl._getHistory = function() {
- return window.history;
- };
-
- // This is exposed so we can mock getting window.location in our tests
- sdk.token.parseFromUrl._getLocation = function() {
- return window.location;
- };
-
- // This is exposed so we can mock getting window.document in our tests
- sdk.token.parseFromUrl._getDocument = function() {
- return window.document;
- };
-
- sdk.fingerprint._getUserAgent = function() {
- return navigator.userAgent;
- };
-
- var isWindowsPhone = /windows phone|iemobile|wpdesktop/i;
- sdk.features.isFingerprintSupported = function() {
- var agent = sdk.fingerprint._getUserAgent();
- return agent && !isWindowsPhone.test(agent);
- };
-
- sdk.tokenManager = new TokenManager(sdk, args.tokenManager);
- }
-
- var proto = OktaAuthBuilder.prototype;
-
- proto.features = {};
-
- proto.features.isPopupPostMessageSupported = function() {
- var isIE8or9 = document.documentMode && document.documentMode < 10;
- if (window.postMessage && !isIE8or9) {
- return true;
- }
- return false;
- };
-
- proto.features.isTokenVerifySupported = function() {
- return typeof crypto !== 'undefined' && crypto.subtle && typeof Uint8Array !== 'undefined';
- };
-
- // { username, password, (relayState), (context) }
- proto.signIn = function (opts) {
- var sdk = this;
- opts = util.clone(opts || {});
- function postToTransaction(options) {
- delete opts.sendFingerprint;
- return tx.postToTransaction(sdk, '/api/v1/authn', opts, options);
- }
- if (!opts.sendFingerprint) {
- return postToTransaction();
- }
- return sdk.fingerprint()
- .then(function(fingerprint) {
- return postToTransaction({
- headers: {
- 'X-Device-Fingerprint': fingerprint
- }
- });
- });
- };
-
- proto.signOut = function () {
- return this.session.close();
- };
-
- // { username, (relayState) }
- proto.forgotPassword = function (opts) {
- return tx.postToTransaction(this, '/api/v1/authn/recovery/password', opts);
- };
-
- // { username, (relayState) }
- proto.unlockAccount = function (opts) {
- return tx.postToTransaction(this, '/api/v1/authn/recovery/unlock', opts);
- };
-
- // { recoveryToken }
- proto.verifyRecoveryToken = function (opts) {
- return tx.postToTransaction(this, '/api/v1/authn/recovery/token', opts);
- };
-
- // { resource, (rel), (requestContext)}
- proto.webfinger = function (opts) {
- var url = '/.well-known/webfinger' + util.toQueryParams(opts);
- var options = {
- headers: {
- 'Accept': 'application/jrd+json'
- }
- };
- return http.get(this, url, options);
- };
-
- proto.fingerprint = function(options) {
- options = options || {};
- var sdk = this;
- if (!sdk.features.isFingerprintSupported()) {
- return Q.reject(new AuthSdkError('Fingerprinting is not supported on this device'));
- }
-
- var deferred = Q.defer();
-
- var iframe = document.createElement('iframe');
- iframe.style.display = 'none';
-
- function listener(e) {
- if (!e || !e.data || e.origin !== sdk.options.url) {
- return;
- }
-
- try {
- var msg = JSON.parse(e.data);
- } catch (err) {
- return deferred.reject(new AuthSdkError('Unable to parse iframe response'));
- }
-
- if (!msg) { return; }
- if (msg.type === 'FingerprintAvailable') {
- return deferred.resolve(msg.fingerprint);
- }
- if (msg.type === 'FingerprintServiceReady') {
- e.source.postMessage(JSON.stringify({
- type: 'GetFingerprint'
- }), e.origin);
- }
- }
- oauthUtil.addListener(window, 'message', listener);
-
- iframe.src = sdk.options.url + '/auth/services/devicefingerprint';
- document.body.appendChild(iframe);
-
- var timeout = setTimeout(function() {
- deferred.reject(new AuthSdkError('Fingerprinting timed out'));
- }, options.timeout || 15000);
-
- return deferred.promise.fin(function() {
- clearTimeout(timeout);
- oauthUtil.removeListener(window, 'message', listener);
- if (document.body.contains(iframe)) {
- iframe.parentElement.removeChild(iframe);
- }
- });
- };
-
- module.exports = function(ajaxRequest) {
- function OktaAuth(args) {
- if (!(this instanceof OktaAuth)) {
- return new OktaAuth(args);
- }
-
- if (args && !args.ajaxRequest) {
- args.ajaxRequest = ajaxRequest;
- }
- util.bind(OktaAuthBuilder, this)(args);
- }
- OktaAuth.prototype = OktaAuthBuilder.prototype;
- OktaAuth.prototype.constructor = OktaAuth;
-
- return OktaAuth;
- };
|