@stacks/auth
Version:
Authentication for Stacks apps.
238 lines • 10.4 kB
JavaScript
;
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