@accounts/server
Advanced tools
Comparing version 0.19.0 to 0.19.1-alpha.2.17
import * as Emittery from 'emittery'; | ||
import { User, LoginResult, Tokens, Session, ImpersonationResult, HookListener, AuthenticationService, ConnectionInformations } from '@accounts/types'; | ||
import { User, LoginResult, MFALoginResult, Tokens, Session, ImpersonationResult, HookListener, AuthenticationService, ConnectionInformations } from '@accounts/types'; | ||
import { AccountsServerOptions } from './types/accounts-server-options'; | ||
@@ -20,2 +20,3 @@ import { EmailTemplateType } from './types/email-template-type'; | ||
siteUrl: string; | ||
createNewSessionTokenOnRefresh: boolean; | ||
}; | ||
@@ -36,3 +37,5 @@ export declare class AccountsServer { | ||
on(eventName: string, callback: HookListener): () => void; | ||
loginWithService(serviceName: string, params: any, infos: ConnectionInformations): Promise<LoginResult>; | ||
authenticateWithService(serviceName: string, params: any, infos: ConnectionInformations): Promise<boolean>; | ||
loginWithService(serviceName: string, params: any, infos: ConnectionInformations): Promise<LoginResult | MFALoginResult>; | ||
performMfaChallenge(challenge: string, mfaToken: string, params: any): Promise<string>; | ||
/** | ||
@@ -117,3 +120,6 @@ * @description Server use only. | ||
private defaultCreateTokenizedUrl; | ||
private createSessionToken; | ||
private getUserFromMfaToken; | ||
private createMfaLoginProcess; | ||
} | ||
export default AccountsServer; |
@@ -25,2 +25,3 @@ "use strict"; | ||
siteUrl: 'http://localhost:3000', | ||
createNewSessionTokenOnRefresh: false, | ||
}; | ||
@@ -62,5 +63,5 @@ var AccountsServer = /** @class */ (function () { | ||
}; | ||
AccountsServer.prototype.loginWithService = function (serviceName, params, infos) { | ||
AccountsServer.prototype.authenticateWithService = function (serviceName, params, infos) { | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var hooksInfo, user, loginResult, err_1; | ||
var hooksInfo, user, err_1; | ||
return tslib_1.__generator(this, function (_a) { | ||
@@ -79,3 +80,3 @@ switch (_a.label) { | ||
case 1: | ||
_a.trys.push([1, 5, , 6]); | ||
_a.trys.push([1, 3, , 4]); | ||
if (!this.services[serviceName]) { | ||
@@ -94,17 +95,73 @@ throw new Error("No service with the name " + serviceName + " was registered."); | ||
} | ||
this.hooks.emit(server_hooks_1.ServerHooks.AuthenticateSuccess, hooksInfo); | ||
return [2 /*return*/, true]; | ||
case 3: | ||
err_1 = _a.sent(); | ||
this.hooks.emit(server_hooks_1.ServerHooks.AuthenticateError, tslib_1.__assign({}, hooksInfo, { error: err_1 })); | ||
throw err_1; | ||
case 4: return [2 /*return*/]; | ||
} | ||
}); | ||
}); | ||
}; | ||
AccountsServer.prototype.loginWithService = function (serviceName, params, infos) { | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var hooksInfo, user, loginResult, err_2; | ||
return tslib_1.__generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
hooksInfo = { | ||
// The service name, such as “password” or “twitter”. | ||
service: serviceName, | ||
// The connection informations <ConnectionInformations> | ||
connection: infos, | ||
// Params received | ||
params: params, | ||
}; | ||
_a.label = 1; | ||
case 1: | ||
_a.trys.push([1, 9, , 10]); | ||
if (serviceName !== 'mfa' && !this.services[serviceName]) { | ||
throw new Error("No service with the name " + serviceName + " was registered."); | ||
} | ||
user = void 0; | ||
if (!(serviceName !== 'mfa')) return [3 /*break*/, 3]; | ||
return [4 /*yield*/, this.services[serviceName].authenticate(params)]; | ||
case 2: | ||
user = _a.sent(); | ||
return [3 /*break*/, 6]; | ||
case 3: return [4 /*yield*/, this.getUserFromMfaToken(tslib_1.__assign({ skipValidation: false }, params))]; | ||
case 4: | ||
user = _a.sent(); | ||
if (!user) return [3 /*break*/, 6]; | ||
return [4 /*yield*/, this.db.removeMfaLoginAttempt(params.mfaToken)]; | ||
case 5: | ||
_a.sent(); | ||
_a.label = 6; | ||
case 6: | ||
hooksInfo.user = user; | ||
if (!user) { | ||
throw new Error("Service " + serviceName + " was not able to authenticate user"); | ||
} | ||
if (user.deactivated) { | ||
throw new Error('Your account has been deactivated'); | ||
} | ||
// Let the user validate the login attempt | ||
return [4 /*yield*/, this.hooks.emitSerial(server_hooks_1.ServerHooks.ValidateLogin, hooksInfo)]; | ||
case 3: | ||
case 7: | ||
// Let the user validate the login attempt | ||
_a.sent(); | ||
if (serviceName !== 'mfa' && user.mfaChallenges && user.mfaChallenges.length > 0) { | ||
return [2 /*return*/, this.createMfaLoginProcess(user)]; | ||
} | ||
return [4 /*yield*/, this.loginWithUser(user, infos)]; | ||
case 4: | ||
case 8: | ||
loginResult = _a.sent(); | ||
this.hooks.emit(server_hooks_1.ServerHooks.LoginSuccess, hooksInfo); | ||
return [2 /*return*/, loginResult]; | ||
case 5: | ||
err_1 = _a.sent(); | ||
this.hooks.emit(server_hooks_1.ServerHooks.LoginError, tslib_1.__assign({}, hooksInfo, { error: err_1 })); | ||
throw err_1; | ||
case 6: return [2 /*return*/]; | ||
case 9: | ||
err_2 = _a.sent(); | ||
this.hooks.emit(server_hooks_1.ServerHooks.LoginError, tslib_1.__assign({}, hooksInfo, { error: err_2 })); | ||
throw err_2; | ||
case 10: return [2 /*return*/]; | ||
} | ||
@@ -114,2 +171,29 @@ }); | ||
}; | ||
AccountsServer.prototype.performMfaChallenge = function (challenge, mfaToken, params) { | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var userFromMfa, userFromChallenge, mfaLoginAttempt; | ||
return tslib_1.__generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: return [4 /*yield*/, this.getUserFromMfaToken({ skipValidation: true, mfaToken: mfaToken })]; | ||
case 1: | ||
userFromMfa = _a.sent(); | ||
if (!userFromMfa || | ||
!userFromMfa.mfaChallenges || | ||
userFromMfa.mfaChallenges.indexOf(challenge) === -1) { | ||
throw new Error('Performing the mfa challenge is not available'); | ||
} | ||
return [4 /*yield*/, this.services[challenge].authenticate(params)]; | ||
case 2: | ||
userFromChallenge = _a.sent(); | ||
if (!userFromChallenge || userFromMfa.id !== userFromChallenge.id) { | ||
throw new Error("Service " + challenge + " was not able to authenticate user"); | ||
} | ||
return [4 /*yield*/, this.db.getMfaLoginAttempt(mfaToken)]; | ||
case 3: | ||
mfaLoginAttempt = (_a.sent()); | ||
return [2 /*return*/, mfaLoginAttempt.loginToken]; | ||
} | ||
}); | ||
}); | ||
}; | ||
/** | ||
@@ -131,3 +215,5 @@ * @description Server use only. | ||
ip = infos.ip, userAgent = infos.userAgent; | ||
token = tokens_1.generateRandomToken(); | ||
return [4 /*yield*/, this.createSessionToken(user)]; | ||
case 1: | ||
token = _b.sent(); | ||
return [4 /*yield*/, this.db.createSession(user.id, token, { | ||
@@ -137,3 +223,3 @@ ip: ip, | ||
})]; | ||
case 1: | ||
case 2: | ||
sessionId = _b.sent(); | ||
@@ -266,7 +352,7 @@ _a = this.createTokens({ | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var sessionToken, decodedAccessToken, session, user, tokens, result, err_2; | ||
var sessionToken, decodedAccessToken, session, user, newToken, tokens, result, err_3; | ||
return tslib_1.__generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
_a.trys.push([0, 6, , 7]); | ||
_a.trys.push([0, 8, , 9]); | ||
if (!lodash_1.isString(accessToken) || !lodash_1.isString(refreshToken)) { | ||
@@ -292,3 +378,3 @@ throw new Error('An accessToken and refreshToken are required'); | ||
} | ||
if (!session.valid) return [3 /*break*/, 4]; | ||
if (!session.valid) return [3 /*break*/, 6]; | ||
return [4 /*yield*/, this.db.findUserById(session.userId)]; | ||
@@ -300,5 +386,12 @@ case 2: | ||
} | ||
tokens = this.createTokens({ token: sessionToken, userId: user.id }); | ||
return [4 /*yield*/, this.db.updateSession(session.id, { ip: ip, userAgent: userAgent })]; | ||
newToken = void 0; | ||
if (!this.options.createNewSessionTokenOnRefresh) return [3 /*break*/, 4]; | ||
return [4 /*yield*/, this.createSessionToken(user)]; | ||
case 3: | ||
newToken = _a.sent(); | ||
_a.label = 4; | ||
case 4: | ||
tokens = this.createTokens({ token: newToken || sessionToken, userId: user.id }); | ||
return [4 /*yield*/, this.db.updateSession(session.id, { ip: ip, userAgent: userAgent }, newToken)]; | ||
case 5: | ||
_a.sent(); | ||
@@ -312,9 +405,9 @@ result = { | ||
return [2 /*return*/, result]; | ||
case 4: throw new Error('Session is no longer valid'); | ||
case 5: return [3 /*break*/, 7]; | ||
case 6: | ||
err_2 = _a.sent(); | ||
this.hooks.emit(server_hooks_1.ServerHooks.RefreshTokensError, err_2); | ||
throw err_2; | ||
case 7: return [2 /*return*/]; | ||
case 6: throw new Error('Session is no longer valid'); | ||
case 7: return [3 /*break*/, 9]; | ||
case 8: | ||
err_3 = _a.sent(); | ||
this.hooks.emit(server_hooks_1.ServerHooks.RefreshTokensError, err_3); | ||
throw err_3; | ||
case 9: return [2 /*return*/]; | ||
} | ||
@@ -518,2 +611,50 @@ }); | ||
}; | ||
AccountsServer.prototype.createSessionToken = function (user) { | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
return tslib_1.__generator(this, function (_a) { | ||
return [2 /*return*/, this.options.tokenCreator | ||
? this.options.tokenCreator.createToken(user) | ||
: tokens_1.generateRandomToken()]; | ||
}); | ||
}); | ||
}; | ||
AccountsServer.prototype.getUserFromMfaToken = function (_a) { | ||
var mfaToken = _a.mfaToken, loginToken = _a.loginToken, _b = _a.skipValidation, skipValidation = _b === void 0 ? false : _b; | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var loginAttempt; | ||
return tslib_1.__generator(this, function (_c) { | ||
switch (_c.label) { | ||
case 0: return [4 /*yield*/, this.db.getMfaLoginAttempt(mfaToken)]; | ||
case 1: | ||
loginAttempt = _c.sent(); | ||
if (!loginAttempt) { | ||
return [2 /*return*/, null]; | ||
} | ||
if (!skipValidation && loginAttempt.loginToken !== loginToken) { | ||
return [2 /*return*/, null]; | ||
} | ||
return [2 /*return*/, this.db.findUserById(loginAttempt.userId)]; | ||
} | ||
}); | ||
}); | ||
}; | ||
AccountsServer.prototype.createMfaLoginProcess = function (user) { | ||
return tslib_1.__awaiter(this, void 0, void 0, function () { | ||
var loginToken, mfaToken; | ||
return tslib_1.__generator(this, function (_a) { | ||
switch (_a.label) { | ||
case 0: | ||
loginToken = tokens_1.generateRandomToken(); | ||
mfaToken = tokens_1.hashToken(loginToken); | ||
return [4 /*yield*/, this.db.createMfaLoginAttempt(mfaToken, loginToken, user.id)]; | ||
case 1: | ||
_a.sent(); | ||
return [2 /*return*/, { | ||
mfaToken: mfaToken, | ||
challenges: user.mfaChallenges, | ||
}]; | ||
} | ||
}); | ||
}); | ||
}; | ||
return AccountsServer; | ||
@@ -520,0 +661,0 @@ }()); |
@@ -8,2 +8,3 @@ import * as jwt from 'jsonwebtoken'; | ||
import { SendMailType } from './send-mail-type'; | ||
import { TokenCreator } from './token-creator'; | ||
export interface AccountsServerOptions { | ||
@@ -27,2 +28,7 @@ /** | ||
sendMail?: SendMailType; | ||
tokenCreator?: TokenCreator; | ||
/** | ||
* Creates a new session token each time a user refreshes his access token | ||
*/ | ||
createNewSessionTokenOnRefresh?: boolean; | ||
} |
@@ -5,2 +5,4 @@ export declare const ServerHooks: { | ||
LoginError: string; | ||
AuthenticateSuccess: string; | ||
AuthenticateError: string; | ||
LogoutSuccess: string; | ||
@@ -7,0 +9,0 @@ LogoutError: string; |
@@ -7,2 +7,4 @@ "use strict"; | ||
LoginError: 'LoginError', | ||
AuthenticateSuccess: 'AuthenticateSuccess', | ||
AuthenticateError: 'AuthenticateError', | ||
LogoutSuccess: 'LogoutSuccess', | ||
@@ -9,0 +11,0 @@ LogoutError: 'LogoutError', |
@@ -6,2 +6,3 @@ import * as jwt from 'jsonwebtoken'; | ||
export declare const generateRandomToken: (length?: number) => string; | ||
export declare const hashToken: (token: string) => string; | ||
export declare const generateAccessToken: ({ secret, data, config, }: { | ||
@@ -8,0 +9,0 @@ secret: string; |
@@ -12,2 +12,7 @@ "use strict"; | ||
}; | ||
exports.hashToken = function (token) { | ||
var hash = crypto_1.createHash('sha256'); | ||
hash.update(token); | ||
return hash.digest('hex'); | ||
}; | ||
exports.generateAccessToken = function (_a) { | ||
@@ -14,0 +19,0 @@ var secret = _a.secret, data = _a.data, config = _a.config; |
{ | ||
"name": "@accounts/server", | ||
"version": "0.19.0", | ||
"version": "0.19.1-alpha.2.17+1d0a3901", | ||
"description": "Fullstack authentication and accounts-management", | ||
@@ -43,3 +43,3 @@ "main": "lib/index.js", | ||
"dependencies": { | ||
"@accounts/types": "^0.19.0", | ||
"@accounts/types": "^0.19.1-alpha.2.17+1d0a3901", | ||
"@types/jsonwebtoken": "8.3.3", | ||
@@ -55,7 +55,7 @@ "emittery": "0.4.1", | ||
"@types/jwt-decode": "2.2.1", | ||
"@types/node": "12.7.2", | ||
"@types/node": "12.7.4", | ||
"jest": "24.9.0", | ||
"rimraf": "3.0.0" | ||
}, | ||
"gitHead": "c0a7905161c702350efd4f7a622c55ede495a42b" | ||
"gitHead": "1d0a3901760c599054c0ccdd3bcb0443e854646a" | ||
} |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Manifest confusion
Supply chain riskThis package has inconsistent metadata. This could be malicious or caused by an error when publishing the package.
Found 1 instance in 1 package
69407
51
1034
1
+ Added@accounts/types@0.19.1-alpha.2.17(transitive)
- Removed@accounts/types@0.19.0(transitive)