Skip to content

Commit d11863d

Browse files
authored
feat: add handler for in-store customer token (#236)
[commercetools doc](https://docs.commercetools.com/api/authorization#password-flow-for-customers-in-a-store) Implement a handler to get the client credential flow for an in-store customer. it works the same as normal customer flow
1 parent ad06557 commit d11863d

3 files changed

Lines changed: 91 additions & 7 deletions

File tree

.changeset/fast-meals-drive.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@labdigital/commercetools-mock": minor
3+
---
4+
5+
add handler for creating in-store customer token

src/oauth/server.test.ts

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -130,4 +130,46 @@ describe("OAuth2Server", () => {
130130
});
131131
});
132132
});
133+
134+
describe("POST /:projectKey/in-store/key=:storeKey/customers/token", () => {
135+
it("should return a token for in-store customer access", async () => {
136+
const projectKey = "test-project";
137+
const storeKey = "test-store";
138+
139+
storage.add(projectKey, "customer", {
140+
...getBaseResourceProperties(),
141+
email: "j.doe@example.org",
142+
password: hashPassword("password"),
143+
addresses: [],
144+
authenticationMode: "password",
145+
isEmailVerified: true,
146+
stores: [
147+
{
148+
typeId: "store",
149+
key: storeKey,
150+
},
151+
],
152+
});
153+
154+
const response = await supertest(app)
155+
.post(`/${projectKey}/in-store/key=${storeKey}/customers/token`)
156+
.auth("validClientId", "validClientSecret")
157+
.query({
158+
grant_type: "password",
159+
username: "j.doe@example.org",
160+
password: "password",
161+
scope: `${projectKey}:manage_my_profile`,
162+
})
163+
.send();
164+
165+
expect(response.status).toBe(200);
166+
expect(response.body).toEqual({
167+
scope: expect.stringMatching(/customer_id:([^\s]+)/),
168+
access_token: expect.stringMatching(/\S{8,}==$/),
169+
refresh_token: expect.stringMatching(/test-project:\S{8,}==$/),
170+
expires_in: 172800,
171+
token_type: "Bearer",
172+
});
173+
});
174+
});
133175
});

src/oauth/server.ts

Lines changed: 44 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -287,15 +287,52 @@ export class OAuth2Server {
287287
response: Response,
288288
next: NextFunction,
289289
) {
290-
return next(
291-
new CommercetoolsError<InvalidClientError>(
290+
const projectKey = request.params.projectKey;
291+
const storeKey = request.params.storeKey;
292+
const grantType = request.query.grant_type || request.body.grant_type;
293+
if (!grantType) {
294+
return next(
295+
new CommercetoolsError<InvalidRequestError>(
296+
{
297+
code: "invalid_request",
298+
message: "Missing required parameter: grant_type.",
299+
},
300+
400,
301+
),
302+
);
303+
}
304+
305+
if (grantType === "password") {
306+
const username = request.query.username || request.body.username;
307+
const password = hashPassword(
308+
request.query.password || request.body.password,
309+
);
310+
const scope =
311+
request.query.scope?.toString() || request.body.scope?.toString();
312+
313+
const result = this.customerRepository.query(
314+
{ projectKey, storeKey },
292315
{
293-
code: "invalid_client",
294-
message: "Not implemented yet in commercetools-mock",
316+
where: [`email = "${username}"`, `password = "${password}"`],
295317
},
296-
401,
297-
),
298-
);
318+
);
319+
320+
if (result.count === 0) {
321+
return next(
322+
new CommercetoolsError<any>(
323+
{
324+
code: "invalid_customer_account_credentials",
325+
message: "Customer account with the given credentials not found.",
326+
},
327+
400,
328+
),
329+
);
330+
}
331+
332+
const customer = result.results[0];
333+
const token = this.store.getCustomerToken(projectKey, customer.id, scope);
334+
return response.status(200).send(token);
335+
}
299336
}
300337

301338
async anonymousTokenHandler(

0 commit comments

Comments
 (0)