UNPKG

@stacks/auth

Version:

Authentication for Stacks apps.

238 lines 10.4 kB
"use strict"; var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { if (k2 === undefined) k2 = k; var desc = Object.getOwnPropertyDescriptor(m, k); if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { desc = { enumerable: true, get: function() { return m[k]; } }; } Object.defineProperty(o, k2, desc); }) : (function(o, m, k, k2) { if (k2 === undefined) k2 = k; o[k2] = m[k]; })); var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { Object.defineProperty(o, "default", { enumerable: true, value: v }); }) : function(o, v) { o["default"] = v; }); var __importStar = (this && this.__importStar) || function (mod) { if (mod && mod.__esModule) return mod; var result = {}; if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); __setModuleDefault(result, mod); return result; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.UserSession = void 0; const appConfig_1 = require("./appConfig"); const sessionStore_1 = require("./sessionStore"); const jsontokens_1 = require("jsontokens"); const verification_1 = require("./verification"); const authMessages = __importStar(require("./messages")); const secp256k1_1 = require("@noble/secp256k1"); const encryption_1 = require("@stacks/encryption"); const dids_1 = require("./dids"); const common_1 = require("@stacks/common"); const profile_1 = require("@stacks/profile"); const constants_1 = require("./constants"); const protocolEchoDetection_1 = require("./protocolEchoDetection"); class UserSession { constructor(options) { let runningInBrowser = true; if (typeof window === 'undefined' && typeof self === 'undefined') { runningInBrowser = false; } if (options && options.appConfig) { this.appConfig = options.appConfig; } else if (runningInBrowser) { this.appConfig = new appConfig_1.AppConfig(); } else { throw new common_1.MissingParameterError('You need to specify options.appConfig'); } if (options && options.sessionStore) { this.store = options.sessionStore; } else if (runningInBrowser) { if (options) { this.store = new sessionStore_1.LocalStorageStore(options.sessionOptions); } else { this.store = new sessionStore_1.LocalStorageStore(); } } else if (options) { this.store = new sessionStore_1.InstanceDataStore(options.sessionOptions); } else { this.store = new sessionStore_1.InstanceDataStore(); } } makeAuthRequestToken(transitKey, redirectURI, manifestURI, scopes, appDomain, expiresAt = (0, common_1.nextHour)().getTime(), extraParams = {}) { const appConfig = this.appConfig; if (!appConfig) { throw new common_1.InvalidStateError('Missing AppConfig'); } transitKey = transitKey || this.generateAndStoreTransitKey(); redirectURI = redirectURI || appConfig.redirectURI(); manifestURI = manifestURI || appConfig.manifestURI(); scopes = scopes || appConfig.scopes; appDomain = appDomain || appConfig.appDomain; return authMessages.makeAuthRequestToken(transitKey, redirectURI, manifestURI, scopes, appDomain, expiresAt, extraParams); } generateAndStoreTransitKey() { const sessionData = this.store.getSessionData(); const transitKey = authMessages.generateTransitKey(); sessionData.transitKey = transitKey; this.store.setSessionData(sessionData); return transitKey; } getAuthResponseToken() { const search = (0, common_1.getGlobalObject)('location', { throwIfUnavailable: true, usageDesc: 'getAuthResponseToken', })?.search; const params = new URLSearchParams(search); return params.get('authResponse') ?? ''; } isSignInPending() { try { const isProtocolEcho = (0, protocolEchoDetection_1.protocolEchoReplyDetection)(); if (isProtocolEcho) { common_1.Logger.info('protocolEchoReply detected from isSignInPending call, the page is about to redirect.'); return true; } } catch (error) { common_1.Logger.error(`Error checking for protocol echo reply isSignInPending: ${error}`); } return !!this.getAuthResponseToken(); } isUserSignedIn() { return !!this.store.getSessionData().userData; } async handlePendingSignIn(authResponseToken = this.getAuthResponseToken(), fetchFn = (0, common_1.createFetchFn)()) { const sessionData = this.store.getSessionData(); if (sessionData.userData) { throw new common_1.LoginFailedError('Existing user session found.'); } const transitKey = this.store.getSessionData().transitKey; let coreNode = this.appConfig && this.appConfig.coreNode; if (!coreNode) { coreNode = common_1.HIRO_MAINNET_URL; } const tokenPayload = (0, jsontokens_1.decodeToken)(authResponseToken).payload; if (typeof tokenPayload === 'string') { throw new Error('Unexpected token payload type of string'); } const isValid = await (0, verification_1.verifyAuthResponse)(authResponseToken); if (!isValid) { throw new common_1.LoginFailedError('Invalid authentication response.'); } let appPrivateKey = tokenPayload.private_key; let coreSessionToken = tokenPayload.core_token; if ((0, common_1.isLaterVersion)(tokenPayload.version, '1.1.0')) { if (transitKey !== undefined && transitKey != null) { if (tokenPayload.private_key !== undefined && tokenPayload.private_key !== null) { try { appPrivateKey = (await authMessages.decryptPrivateKey(transitKey, tokenPayload.private_key)); } catch (e) { common_1.Logger.warn('Failed decryption of appPrivateKey, will try to use as given'); if (!secp256k1_1.utils.isValidPrivateKey(tokenPayload.private_key)) { throw new common_1.LoginFailedError('Failed decrypting appPrivateKey. Usually means' + ' that the transit key has changed during login.'); } } } if (coreSessionToken !== undefined && coreSessionToken !== null) { try { coreSessionToken = (await authMessages.decryptPrivateKey(transitKey, coreSessionToken)); } catch (e) { common_1.Logger.info('Failed decryption of coreSessionToken, will try to use as given'); } } } else { throw new common_1.LoginFailedError('Authenticating with protocol > 1.1.0 requires transit' + ' key, and none found.'); } } let hubUrl = common_1.GAIA_URL; let gaiaAssociationToken; if ((0, common_1.isLaterVersion)(tokenPayload.version, '1.2.0') && tokenPayload.hubUrl !== null && tokenPayload.hubUrl !== undefined) { hubUrl = tokenPayload.hubUrl; } if ((0, common_1.isLaterVersion)(tokenPayload.version, '1.3.0') && tokenPayload.associationToken !== null && tokenPayload.associationToken !== undefined) { gaiaAssociationToken = tokenPayload.associationToken; } const userData = { profile: tokenPayload.profile, email: tokenPayload.email, decentralizedID: tokenPayload.iss, identityAddress: (0, dids_1.getAddressFromDID)(tokenPayload.iss), appPrivateKey, coreSessionToken, authResponseToken, hubUrl, appPrivateKeyFromWalletSalt: tokenPayload.appPrivateKeyFromWalletSalt, coreNode: tokenPayload.blockstackAPIUrl, gaiaAssociationToken, }; const profileURL = tokenPayload.profile_url; if (!userData.profile && profileURL) { const response = await fetchFn(profileURL); if (!response.ok) { userData.profile = Object.assign({}, constants_1.DEFAULT_PROFILE); } else { const responseText = await response.text(); const wrappedProfile = JSON.parse(responseText); userData.profile = (0, profile_1.extractProfile)(wrappedProfile[0].token); } } else { userData.profile = tokenPayload.profile; } sessionData.userData = userData; this.store.setSessionData(sessionData); return userData; } loadUserData() { const userData = this.store.getSessionData().userData; if (!userData) { throw new common_1.InvalidStateError('No user data found. Did the user sign in?'); } return userData; } encryptContent(content, options) { const opts = Object.assign({}, options); if (!opts.privateKey) { opts.privateKey = this.loadUserData().appPrivateKey; } return (0, encryption_1.encryptContent)(content, opts); } decryptContent(content, options) { const opts = Object.assign({}, options); if (!opts.privateKey) { opts.privateKey = this.loadUserData().appPrivateKey; } return (0, encryption_1.decryptContent)(content, opts); } signUserOut(redirectURL) { this.store.deleteSessionData(); if (redirectURL) { if (typeof location !== 'undefined' && location.href) { location.href = redirectURL; } } } } exports.UserSession = UserSession; UserSession.prototype.makeAuthRequest = UserSession.prototype.makeAuthRequestToken; //# sourceMappingURL=userSession.js.map