From fefbfc78d328ead616e85978e917eba8f3e26e10 Mon Sep 17 00:00:00 2001 From: Sara Ghaedi Date: Mon, 21 Oct 2024 11:41:04 +0200 Subject: [PATCH 1/2] feat: add handler for in-store customer token --- src/oauth/server.test.ts | 42 +++++++++++++++++++++++++++++++++ src/oauth/server.ts | 51 ++++++++++++++++++++++++++++++++++------ 2 files changed, 86 insertions(+), 7 deletions(-) diff --git a/src/oauth/server.test.ts b/src/oauth/server.test.ts index 65810fd..9317e49 100644 --- a/src/oauth/server.test.ts +++ b/src/oauth/server.test.ts @@ -130,4 +130,46 @@ describe("OAuth2Server", () => { }); }); }); + + describe("POST /:projectKey/in-store/key=:storeKey/customers/token", () => { + it("should return a token for in-store customer access", async () => { + const projectKey = "test-project"; + const storeKey = "test-store"; + + storage.add(projectKey, "customer", { + ...getBaseResourceProperties(), + email: "j.doe@example.org", + password: hashPassword("password"), + addresses: [], + authenticationMode: "password", + isEmailVerified: true, + stores: [ + { + typeId: "store", + key: storeKey, + }, + ], + }); + + const response = await supertest(app) + .post(`/${projectKey}/in-store/key=${storeKey}/customers/token`) + .auth("validClientId", "validClientSecret") + .query({ + grant_type: "password", + username: "j.doe@example.org", + password: "password", + scope: `${projectKey}:manage_my_profile`, + }) + .send(); + + expect(response.status).toBe(200); + expect(response.body).toEqual({ + scope: expect.stringMatching(/customer_id:([^\s]+)/), + access_token: expect.stringMatching(/\S{8,}==$/), + refresh_token: expect.stringMatching(/test-project:\S{8,}==$/), + expires_in: 172800, + token_type: "Bearer", + }); + }); + }); }); diff --git a/src/oauth/server.ts b/src/oauth/server.ts index b1a7e0a..26af868 100644 --- a/src/oauth/server.ts +++ b/src/oauth/server.ts @@ -287,15 +287,52 @@ export class OAuth2Server { response: Response, next: NextFunction, ) { - return next( - new CommercetoolsError( + const projectKey = request.params.projectKey; + const storeKey = request.params.storeKey; + const grantType = request.query.grant_type || request.body.grant_type; + if (!grantType) { + return next( + new CommercetoolsError( + { + code: "invalid_request", + message: "Missing required parameter: grant_type.", + }, + 400, + ), + ); + } + + if (grantType === "password") { + const username = request.query.username || request.body.username; + const password = hashPassword( + request.query.password || request.body.password, + ); + const scope = + request.query.scope?.toString() || request.body.scope?.toString(); + + const result = this.customerRepository.query( + { projectKey, storeKey }, { - code: "invalid_client", - message: "Not implemented yet in commercetools-mock", + where: [`email = "${username}"`, `password = "${password}"`], }, - 401, - ), - ); + ); + + if (result.count === 0) { + return next( + new CommercetoolsError( + { + code: "invalid_customer_account_credentials", + message: "Customer account with the given credentials not found.", + }, + 400, + ), + ); + } + + const customer = result.results[0]; + const token = this.store.getCustomerToken(projectKey, customer.id, scope); + return response.status(200).send(token); + } } async anonymousTokenHandler( From 33df6776d68da576c1229b75dbbc2cc716c3823b Mon Sep 17 00:00:00 2001 From: Sara Ghaedi Date: Mon, 21 Oct 2024 11:52:24 +0200 Subject: [PATCH 2/2] chore: add changeset --- .changeset/fast-meals-drive.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 .changeset/fast-meals-drive.md diff --git a/.changeset/fast-meals-drive.md b/.changeset/fast-meals-drive.md new file mode 100644 index 0000000..f1a81ff --- /dev/null +++ b/.changeset/fast-meals-drive.md @@ -0,0 +1,5 @@ +--- +"@labdigital/commercetools-mock": minor +--- + +add handler for creating in-store customer token