diff --git a/package.json b/package.json
index eae1f3d..af91e4a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@logion/rest-api-core",
- "version": "0.5.2",
+ "version": "0.6.0-1",
"repository": {
"type": "git",
"url": "git+https://github.com/logion-network/logion-rest-api-core.git"
@@ -26,7 +26,7 @@
"coverage": "nyc yarn run test"
},
"dependencies": {
- "@logion/authenticator": "^0.6.2",
+ "@logion/authenticator": "^0.7.0-1",
"dinoloop": "^2.4.0",
"express": "^4.18.2",
"express-fileupload": "^1.4.0",
@@ -39,7 +39,7 @@
},
"devDependencies": {
"@istanbuljs/nyc-config-typescript": "^1.0.2",
- "@logion/node-api": "^0.31.0",
+ "@logion/node-api": "^0.31.2",
"@tsconfig/node16": "^16.1.1",
"@types/express": "^4.17.14",
"@types/express-fileupload": "^1.4.1",
diff --git a/src/AuthenticationController.ts b/src/AuthenticationController.ts
index c3da6e7..40f2c54 100644
--- a/src/AuthenticationController.ts
+++ b/src/AuthenticationController.ts
@@ -33,6 +33,7 @@ export function fillInSpecForAuthenticationController(spec: OpenAPIV3.Document):
AuthenticationController.signIn(spec);
AuthenticationController.authenticate(spec);
+ AuthenticationController.authenticateV2(spec);
}
@injectable()
@@ -114,6 +115,34 @@ export class AuthenticationController extends ApiController {
return this.toAuthenticateResponseView(tokens);
}
+ static authenticateV2(spec: OpenAPIV3.Document) {
+ const operationObject = requireDefined(spec.paths["/api/auth/{sessionId}/authenticate/v2"].post);
+ operationObject.summary = "Authenticate the given session";
+ operationObject.description = "
The signature's resource is authentication
, the operation login
and the additional field is sessionId
";
+ operationObject.requestBody = getRequestBody({
+ description: "Authentication data",
+ view: "AuthenticateRequestView",
+ });
+ operationObject.responses = getDefaultResponses("AuthenticateResponseView");
+ setPathParameters(operationObject, { 'sessionId': "The ID of the session to authenticate" });
+ }
+
+ @HttpPost('/:sessionId/authenticate/v2')
+ @Async()
+ async authenticateV2(
+ authenticateRequest: AuthenticateRequestView,
+ sessionId: string
+ ): Promise {
+
+ const { session, signatures } = await this.sessionAndSignatures(authenticateRequest, sessionId);
+ const { sessionManager, authenticator } = await this.authenticationService.authenticationSystem();
+
+ const signedSession = await sessionManager.signedSessionOrThrowV2(session, signatures);
+ const tokens = await authenticator.createTokens(signedSession, DateTime.now());
+
+ return this.toAuthenticateResponseView(tokens);
+ }
+
private async sessionAndSignatures(
authenticateRequest: AuthenticateRequestView,
sessionId: string
diff --git a/test/AuthenticationController.spec.ts b/test/AuthenticationController.spec.ts
index f9390f9..dcdb2aa 100644
--- a/test/AuthenticationController.spec.ts
+++ b/test/AuthenticationController.spec.ts
@@ -36,7 +36,7 @@ const TOKEN_ALICE = "some-fake-token-for-ALICE";
const TOKEN_BOB = "some-fake-token-for-BOB";
const SESSION_ID = "a4dade1d-f12c-414c-93f7-7f20ce1e2cb8";
-describe("AuthenticationController", () => {
+describe("AuthenticationController (sign-in)", () => {
it('should sign-in successfully', async () => {
const app = setupApp(AuthenticationController, mockDependenciesForSignIn);
@@ -55,6 +55,12 @@ describe("AuthenticationController", () => {
});
});
+})
+
+describe("AuthenticationController (authentication)", () => {
+
+ const version = "V1";
+
it('should authenticate successfully', async () => {
const authenticateRequest: AuthenticateRequestView = {
@@ -70,7 +76,7 @@ describe("AuthenticationController", () => {
signedOn: TIMESTAMP,
type: "POLKADOT",
};
- const app = setupApp(AuthenticationController, (container) => mockDependenciesForAuth(container,true, true));
+ const app = setupApp(AuthenticationController, (container) => mockDependenciesForAuth(container, true, true, version));
await request(app)
.post(`/api/auth/${SESSION_ID}/authenticate`)
.send(authenticateRequest)
@@ -98,7 +104,7 @@ describe("AuthenticationController", () => {
signedOn: TIMESTAMP,
type: "POLKADOT",
};
- const app = setupApp(AuthenticationController, (container) => mockDependenciesForAuth(container,false, true));
+ const app = setupApp(AuthenticationController, (container) => mockDependenciesForAuth(container, false, true, version));
await request(app)
.post(`/api/auth/${SESSION_ID}/authenticate`)
.send(authenticateRequest)
@@ -124,7 +130,7 @@ describe("AuthenticationController", () => {
signedOn: TIMESTAMP,
type: "POLKADOT",
};
- const app = setupApp(AuthenticationController, (container) => mockDependenciesForAuth(container,true, false));
+ const app = setupApp(AuthenticationController, (container) => mockDependenciesForAuth(container, true, false, version));
await request(app)
.post(`/api/auth/${SESSION_ID}/authenticate`)
.send(authenticateRequest)
@@ -150,7 +156,7 @@ describe("AuthenticationController", () => {
signedOn: TIMESTAMP,
type: "POLKADOT",
};
- const app = setupApp(AuthenticationController, (container) => mockDependenciesForAuth(container,false, true));
+ const app = setupApp(AuthenticationController, (container) => mockDependenciesForAuth(container, false, true, version));
await request(app)
.post(`/api/auth/${SESSION_ID}/authenticate`)
.send(authenticateRequest)
@@ -162,6 +168,119 @@ describe("AuthenticationController", () => {
})
+});
+
+describe("AuthenticationController (authentication v2)", () => {
+
+ const version = "V2";
+
+ it('should authenticate successfully', async () => {
+
+ const authenticateRequest: AuthenticateRequestView = {
+ signatures: {}
+ };
+ authenticateRequest.signatures![ALICE.toKey()] = {
+ signature: "signature-ALICE",
+ signedOn: TIMESTAMP,
+ type: "POLKADOT",
+ };
+ authenticateRequest.signatures![BOB.toKey()] = {
+ signature: "signature-BOB",
+ signedOn: TIMESTAMP,
+ type: "POLKADOT",
+ };
+ const app = setupApp(AuthenticationController, (container) => mockDependenciesForAuth(container, true, true, version));
+ await request(app)
+ .post(`/api/auth/${SESSION_ID}/authenticate/v2`)
+ .send(authenticateRequest)
+ .expect(200)
+ .expect('Content-Type', /application\/json/)
+ .then(response => {
+ expect(response.body.tokens).toBeDefined();
+ expect(response.body.tokens[ALICE.toKey()].value).toBe(TOKEN_ALICE);
+ expect(response.body.tokens[BOB.toKey()].value).toBe(TOKEN_BOB);
+ });
+ })
+
+ it('should fail to authenticate on wrong signature', async () => {
+
+ const authenticateRequest: AuthenticateRequestView = {
+ signatures: {}
+ };
+ authenticateRequest.signatures![ALICE.toKey()] = {
+ signature: "signature-ALICE",
+ signedOn: TIMESTAMP,
+ type: "POLKADOT",
+ };
+ authenticateRequest.signatures![BOB.toKey()] = {
+ signature: "signature-BOB",
+ signedOn: TIMESTAMP,
+ type: "POLKADOT",
+ };
+ const app = setupApp(AuthenticationController, (container) => mockDependenciesForAuth(container, false, true, version));
+ await request(app)
+ .post(`/api/auth/${SESSION_ID}/authenticate/v2`)
+ .send(authenticateRequest)
+ .expect(401)
+ .expect('Content-Type', /application\/json/)
+ .then(response => {
+ expect(response.body.error).toBe("Invalid signature");
+ });
+ })
+
+ it('should fail to authenticate on missing session', async () => {
+
+ const authenticateRequest: AuthenticateRequestView = {
+ signatures: {}
+ };
+ authenticateRequest.signatures![ALICE.toKey()] = {
+ signature: "signature-ALICE",
+ signedOn: TIMESTAMP,
+ type: "POLKADOT",
+ };
+ authenticateRequest.signatures![BOB.toKey()] = {
+ signature: "signature-BOB",
+ signedOn: TIMESTAMP,
+ type: "POLKADOT",
+ };
+ const app = setupApp(AuthenticationController, (container) => mockDependenciesForAuth(container, true, false, version));
+ await request(app)
+ .post(`/api/auth/${SESSION_ID}/authenticate/v2`)
+ .send(authenticateRequest)
+ .expect(401)
+ .expect('Content-Type', /application\/json/)
+ .then(response => {
+ expect(response.body.error).toBe("Invalid session");
+ });
+ })
+
+ it('should fail to authenticate on wrong address type', async () => {
+
+ const authenticateRequest: AuthenticateRequestView = {
+ signatures: {}
+ };
+ authenticateRequest.signatures![`Unknown:${ ALICE.address }`] = {
+ signature: "signature-ALICE",
+ signedOn: TIMESTAMP,
+ type: "POLKADOT",
+ };
+ authenticateRequest.signatures![`Unknown:${ BOB.address }`] = {
+ signature: "signature-BOB",
+ signedOn: TIMESTAMP,
+ type: "POLKADOT",
+ };
+ const app = setupApp(AuthenticationController, (container) => mockDependenciesForAuth(container, false, true, version));
+ await request(app)
+ .post(`/api/auth/${SESSION_ID}/authenticate/v2`)
+ .send(authenticateRequest)
+ .expect(401)
+ .expect('Content-Type', /application\/json/)
+ .then(response => {
+ expect(response.body.error).toBe("Error: Unsupported key format");
+ });
+ })
+
+
});
function mockDependenciesForSignIn(container: Container): void {
@@ -191,7 +310,7 @@ function mockDependenciesForSignIn(container: Container): void {
.returns(() => Promise.resolve());
}
-function mockDependenciesForAuth(container: Container, verifies: boolean, sessionExists:boolean): void {
+function mockDependenciesForAuth(container: Container, verifies: boolean, sessionExists:boolean, version: "V1" | "V2"): void {
const sessionAlice = new Mock();
sessionAlice.setup(instance => instance.createdOn).returns(DateTime.now().toJSDate());
@@ -238,10 +357,17 @@ function mockDependenciesForAuth(container: Container, verifies: boolean, sessio
type: "POLKADOT",
}
];
- sessionManager.setup(instance => instance.signedSessionOrThrow(It.IsAny(), It.IsAny())).returnsAsync({
- session: session.object(),
- signatures
- });
+ if (version === "V1") {
+ sessionManager.setup(instance => instance.signedSessionOrThrow(It.IsAny(), It.IsAny())).returnsAsync({
+ session: session.object(),
+ signatures
+ });
+ } else {
+ sessionManager.setup(instance => instance.signedSessionOrThrowV2(It.IsAny(), It.IsAny())).returnsAsync({
+ session: session.object(),
+ signatures
+ });
+ }
const tokens: Token[] = [
{
type: "Polkadot",
@@ -260,8 +386,17 @@ function mockDependenciesForAuth(container: Container, verifies: boolean, sessio
args => args.session === session.object() && args.signatures === signatures
), It.IsAny())).returnsAsync(tokens);
} else {
- sessionManager.setup(instance => instance.signedSessionOrThrow)
- .returns(() => { throw new UnauthorizedException({error: "Invalid signature"}) });
+ if (version === "V1") {
+ sessionManager.setup(instance => instance.signedSessionOrThrow)
+ .returns(() => {
+ throw new UnauthorizedException({ error: "Invalid signature" })
+ });
+ } else {
+ sessionManager.setup(instance => instance.signedSessionOrThrowV2)
+ .returns(() => {
+ throw new UnauthorizedException({ error: "Invalid signature" })
+ });
+ }
}
const sessionRepository = new Mock();
diff --git a/yarn.lock b/yarn.lock
index 874607b..73fc797 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -564,9 +564,9 @@ __metadata:
languageName: node
linkType: hard
-"@logion/authenticator@npm:^0.6.2":
- version: 0.6.2
- resolution: "@logion/authenticator@npm:0.6.2"
+"@logion/authenticator@npm:^0.7.0-1":
+ version: 0.7.0-1
+ resolution: "@logion/authenticator@npm:0.7.0-1"
dependencies:
"@ethersproject/transactions": ^5.7.0
"@multiversx/sdk-core": ^12.19.1
@@ -578,13 +578,13 @@ __metadata:
web3-utils: ^4.2.1
peerDependencies:
"@logion/node-api": 0.x
- checksum: 46a5fbe4b671255c78c5bf4ff6799f0903783f2c2457023c8862e8360d9e6736cfb3a2a707325bf33f2b47f9ac92a2bdcf80b48c1f4b0fb80659e9a8e8c9d32e
+ checksum: 004f7f91d75cd7fee80886fce7dc3de6ab9d2e853f3126abb163d67c313dfd91958b8c39a2a71f0d73c1fe6b9e42057394bd90216ceaac333ac75aa100f5cb63
languageName: node
linkType: hard
-"@logion/node-api@npm:^0.31.0":
- version: 0.31.0
- resolution: "@logion/node-api@npm:0.31.0"
+"@logion/node-api@npm:^0.31.2":
+ version: 0.31.2
+ resolution: "@logion/node-api@npm:0.31.2"
dependencies:
"@polkadot/api": ^11.0.2
"@polkadot/util": ^12.6.2
@@ -593,7 +593,7 @@ __metadata:
bech32: ^2.0.0
fast-sha256: ^1.3.0
uuid: ^9.0.0
- checksum: 6aae4f45477cefcf253354ac26788a72e705d953f9e7a62f1fae2b92bb1ce9331bc850a967a5c5e1e3103dae448134ae2e1fc710a7408cebf88286b71b4a0ea2
+ checksum: 9d3d41d250db88e49e17da8c3a5dd65271ea54f9ee06c87ac465c344d0e2ec48a4de0c5975745f21d1b8684d682b858d893512eda7a7d1f6d1f81c2518101e2f
languageName: node
linkType: hard
@@ -602,8 +602,8 @@ __metadata:
resolution: "@logion/rest-api-core@workspace:."
dependencies:
"@istanbuljs/nyc-config-typescript": ^1.0.2
- "@logion/authenticator": ^0.6.2
- "@logion/node-api": ^0.31.0
+ "@logion/authenticator": ^0.7.0-1
+ "@logion/node-api": ^0.31.2
"@tsconfig/node16": ^16.1.1
"@types/express": ^4.17.14
"@types/express-fileupload": ^1.4.1