Skip to content

Commit d5593ad

Browse files
authored
feat: phone auth + twilio integration (#58)
* feat: phone auth + twilio integration * fix subscriber * update to latest
1 parent 754b181 commit d5593ad

31 files changed

+13397
-0
lines changed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@ To learn how to use an example, open its `README.md` file. You'll find the detai
2020
| [Loyalty Points System](./loyalty-points/README.md) | Allow customers to earn and redeem loyalty points. |
2121
| [Marketplace](./marketplace/README.md) | Allow vendors to register and sell products. |
2222
| [Migrate from Magento](./migrate-from-magento/README.md) | Migrate products and categories from Magento |
23+
| [Phone Authentication + Twilio SMS Integration](./phone-auth/README.md) | Authenticate users with their phone number + send OTPs with Twilio. |
2324
| [Product Reviews](./product-reviews/README.md) | Allow customers to add product reviews, and merchants to manage them. |
2425
| [Quotes Management](./quotes-management/README.md) | Allow customers to send quotes, and merchants to manage and accept them. |
2526
| [Re-order Feature](./re-order/README.md) | Allow customers to re-order a previous order. |

phone-auth/.env.template

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
STORE_CORS=http://localhost:8000,https://docs.medusajs.com
2+
ADMIN_CORS=http://localhost:5173,http://localhost:9000,https://docs.medusajs.com
3+
AUTH_CORS=http://localhost:5173,http://localhost:9000,https://docs.medusajs.com
4+
REDIS_URL=redis://localhost:6379
5+
JWT_SECRET=supersecret
6+
COOKIE_SECRET=supersecret
7+
DATABASE_URL=postgres://postgres@localhost/$DB_NAME # change user and password if necessary
8+
DB_NAME=medusa-phone-auth
9+
POSTGRES_URL=
10+
11+
TWILIO_ACCOUNT_SID=
12+
TWILIO_AUTH_TOKEN=
13+
TWILIO_FROM=

phone-auth/.gitignore

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
/dist
2+
.env
3+
.DS_Store
4+
/uploads
5+
/node_modules
6+
yarn-error.log
7+
8+
.idea
9+
10+
coverage
11+
12+
!src/**
13+
14+
./tsconfig.tsbuildinfo
15+
medusa-db.sql
16+
build
17+
.cache
18+
19+
.yarn/*
20+
!.yarn/patches
21+
!.yarn/plugins
22+
!.yarn/releases
23+
!.yarn/sdks
24+
!.yarn/versions
25+
26+
.medusa

phone-auth/.yarnrc.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
nodeLinker: node-modules

phone-auth/README.md

Lines changed: 201 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,201 @@
1+
# Medusa v2 Example: Phone Authentication + Twilio SMS Integration
2+
3+
This directory holds the code for the [Phone Authentication Tutorial](https://docs.medusajs.com/resources/how-to-tutorials/tutorials/phone-auth).
4+
5+
> You can use both the Phone Authentication Module Provider and Twilio SMS Notification Module Provider, or just one of them. They're not tightly coupled and there are no side effects to not using them together. However, if you don't use Twilio SMS, you need another way to send the OTP to users.
6+
7+
You can either:
8+
9+
- [install and use it as a Medusa application](#installation);
10+
- or [copy its source files into an existing Medusa application](#copy-into-existing-medusa-application).
11+
12+
## Prerequisites
13+
14+
- [Node.js v20+](https://nodejs.org/en/download)
15+
- [Git CLI](https://git-scm.com/downaloads)
16+
- [PostgreSQL](https://www.postgresql.org/download/)
17+
- If you're using Twilio, you need:
18+
- [Twilio account](https://console.twilio.com/)
19+
- [Phone number](https://www.twilio.com/docs/phone-numbers)
20+
- [Account SID](https://www.twilio.com/docs/usage/tutorials/how-to-use-your-free-trial-account-namer#console-dashboard-home-page)
21+
- [Auth token](https://www.twilio.com/docs/usage/tutorials/how-to-use-your-free-trial-account-namer#console-dashboard-home-page)
22+
23+
## Installation
24+
25+
1. Clone the repository and change to the `phone-auth` directory:
26+
27+
```bash
28+
git clone https://github.com/medusajs/examples.git
29+
cd examples/phone-auth
30+
```
31+
32+
2\. Rename the `.env.template` file to `.env`.
33+
34+
3\. If necessary, change the PostgreSQL username, password, and host in the `DATABASE_URL` environment variable.
35+
36+
4\. Set the Twilio environment variables:
37+
38+
```bash
39+
TWILIO_ACCOUNT_SID=
40+
TWILIO_AUTH_TOKEN=
41+
TWILIO_FROM=
42+
```
43+
44+
Where:
45+
46+
- `TWILIO_ACCOUNT_SID` is the account SID
47+
- `TWILIO_AUTH_TOKEN` is the auth token
48+
- `TWILIO_FROM` is the phone number to send SMS from.
49+
50+
5\. Install dependencies:
51+
52+
```bash
53+
yarn # or npm install
54+
```
55+
56+
6\. Setup and seed the database:
57+
58+
```bash
59+
npx medusa db:setup
60+
yarn seed # or npm run seed
61+
```
62+
63+
7\. Start the Medusa application:
64+
65+
```bash
66+
yarn dev # or npm run dev
67+
```
68+
69+
You can test out the phone authentication and Twilio SMS integration using the OpenAPI Specs/Postman collection in the [more resources](#more-resources) section.
70+
71+
### Use Phone Authentication Module Provider with Actor Types
72+
73+
This project is configured to allow only customers to authenticate with the `phone-auth` provider. To enable it for other actor types, such as admin user or vendors, change the `projectConfig.http.authMethodsPerActor` configuration in `medusa-config.ts`:
74+
75+
```ts
76+
module.exports = defineConfig({
77+
projectConfig: {
78+
// ...
79+
http: {
80+
// ...
81+
authMethodsPerActor: {
82+
user: ["emailpass", "phone-auth"], // enable for admin users
83+
customer: ["emailpass", "phone-auth"],
84+
},
85+
},
86+
},
87+
// ...
88+
})
89+
```
90+
91+
## Copy into Existing Medusa Application
92+
93+
If you have an existing Medusa application, copy the following directories and files into your project:
94+
95+
- `src/api/middlewares.ts`
96+
- `src/modules/phone-auth`
97+
- `src/modules/twilio-sms`
98+
- `src/subscribers`
99+
100+
Then, add the providers to `medusa-config.ts`:
101+
102+
```ts
103+
module.exports = defineConfig({
104+
// ...
105+
modules: [
106+
{
107+
resolve: "@medusajs/medusa/auth",
108+
dependencies: [Modules.CACHE, ContainerRegistrationKeys.LOGGER, Modules.EVENT_BUS],
109+
options: {
110+
providers: [
111+
// default provider
112+
{
113+
resolve: "@medusajs/medusa/auth-emailpass",
114+
id: "emailpass",
115+
},
116+
{
117+
resolve: "./src/modules/phone-auth",
118+
id: "phone-auth",
119+
options: {
120+
jwtSecret: process.env.PHONE_AUTH_JWT_SECRET || "supersecret",
121+
},
122+
},
123+
],
124+
},
125+
},
126+
{
127+
resolve: "@medusajs/medusa/notification",
128+
options: {
129+
providers: [
130+
// default provider
131+
{
132+
resolve: "@medusajs/medusa/notification-local",
133+
id: "local",
134+
options: {
135+
name: "Local Notification Provider",
136+
channels: ["feed"],
137+
},
138+
},
139+
{
140+
resolve: "./src/modules/twilio-sms",
141+
id: "twilio-sms",
142+
options: {
143+
channels: ["sms"],
144+
accountSid: process.env.TWILIO_ACCOUNT_SID,
145+
authToken: process.env.TWILIO_AUTH_TOKEN,
146+
from: process.env.TWILIO_FROM,
147+
},
148+
},
149+
],
150+
},
151+
}
152+
]
153+
})
154+
```
155+
156+
Next, add the following environment variables:
157+
158+
```bash
159+
TWILIO_ACCOUNT_SID=
160+
TWILIO_AUTH_TOKEN=
161+
TWILIO_FROM=
162+
```
163+
164+
Where:
165+
166+
- `TWILIO_ACCOUNT_SID` is the account SID
167+
- `TWILIO_AUTH_TOKEN` is the auth token
168+
- `TWILIO_FROM` is the phone number to send SMS from.
169+
170+
Also, add in `projectConfig.http.authMethodsPerActor` the actor types to enable the `phone-auth` provider for customers:
171+
172+
```ts
173+
module.exports = defineConfig({
174+
projectConfig: {
175+
// ...
176+
http: {
177+
// ...
178+
authMethodsPerActor: {
179+
user: ["emailpass"],
180+
customer: ["emailpass", "phone-auth"],
181+
},
182+
},
183+
},
184+
// ...
185+
})
186+
```
187+
188+
You can also enable it for other actor types (admin user, vendor, etc...)
189+
190+
After that, install the `twilio` and `jsonwebtoken` dependencies package:
191+
192+
```bash
193+
yarn add twilio jsonwebtoken # or npm install twilio jsonwebtoken
194+
yarn add @types/jsonwebtoken -D # or npm install @types/jsonwebtoken --save-dev
195+
```
196+
197+
## More Resources
198+
199+
- [Medusa Documentatin](https://docs.medusajs.com)
200+
- [Twilio Documentation](https://www.twilio.com/docs)
201+
- [OpenAPI Spec file](https://res.cloudinary.com/dza7lstvk/raw/upload/v1747745832/OpenApi/Phone_Auth_g4xsqv.yaml): Can be imported into tools like Postman to view and send requests to this project's API routes.

phone-auth/instrumentation.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
// Uncomment this file to enable instrumentation and observability using OpenTelemetry
2+
// Refer to the docs for installation instructions: https://docs.medusajs.com/learn/debugging-and-testing/instrumentation
3+
4+
// import { registerOtel } from "@medusajs/medusa"
5+
// // If using an exporter other than Zipkin, require it here.
6+
// import { ZipkinExporter } from "@opentelemetry/exporter-zipkin"
7+
8+
// // If using an exporter other than Zipkin, initialize it here.
9+
// const exporter = new ZipkinExporter({
10+
// serviceName: 'my-medusa-project',
11+
// })
12+
13+
// export function register() {
14+
// registerOtel({
15+
// serviceName: 'medusajs',
16+
// // pass exporter
17+
// exporter,
18+
// instrument: {
19+
// http: true,
20+
// workflows: true,
21+
// query: true
22+
// },
23+
// })
24+
// }
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
# Integration Tests
2+
3+
The `medusa-test-utils` package provides utility functions to create integration tests for your API routes and workflows.
4+
5+
For example:
6+
7+
```ts
8+
import { medusaIntegrationTestRunner } from "medusa-test-utils"
9+
10+
medusaIntegrationTestRunner({
11+
testSuite: ({ api, getContainer }) => {
12+
describe("Custom endpoints", () => {
13+
describe("GET /store/custom", () => {
14+
it("returns correct message", async () => {
15+
const response = await api.get(
16+
`/store/custom`
17+
)
18+
19+
expect(response.status).toEqual(200)
20+
expect(response.data).toHaveProperty("message")
21+
expect(response.data.message).toEqual("Hello, World!")
22+
})
23+
})
24+
})
25+
}
26+
})
27+
```
28+
29+
Learn more in [this documentation](https://docs.medusajs.com/learn/debugging-and-testing/testing-tools/integration-tests).
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
import { medusaIntegrationTestRunner } from "@medusajs/test-utils"
2+
jest.setTimeout(60 * 1000)
3+
4+
medusaIntegrationTestRunner({
5+
inApp: true,
6+
env: {},
7+
testSuite: ({ api }) => {
8+
describe("Ping", () => {
9+
it("ping the server health endpoint", async () => {
10+
const response = await api.get('/health')
11+
expect(response.status).toEqual(200)
12+
})
13+
})
14+
},
15+
})

phone-auth/integration-tests/setup.js

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
const { MetadataStorage } = require("@mikro-orm/core")
2+
3+
MetadataStorage.clear()

phone-auth/jest.config.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
const { loadEnv } = require("@medusajs/utils");
2+
loadEnv("test", process.cwd());
3+
4+
module.exports = {
5+
transform: {
6+
"^.+\\.[jt]s$": [
7+
"@swc/jest",
8+
{
9+
jsc: {
10+
parser: { syntax: "typescript", decorators: true },
11+
},
12+
},
13+
],
14+
},
15+
testEnvironment: "node",
16+
moduleFileExtensions: ["js", "ts", "json"],
17+
modulePathIgnorePatterns: ["dist/", "<rootDir>/.medusa/"],
18+
setupFiles: ["./integration-tests/setup.js"],
19+
};
20+
21+
if (process.env.TEST_TYPE === "integration:http") {
22+
module.exports.testMatch = ["**/integration-tests/http/*.spec.[jt]s"];
23+
} else if (process.env.TEST_TYPE === "integration:modules") {
24+
module.exports.testMatch = ["**/src/modules/*/__tests__/**/*.[jt]s"];
25+
} else if (process.env.TEST_TYPE === "unit") {
26+
module.exports.testMatch = ["**/src/**/__tests__/**/*.unit.spec.[jt]s"];
27+
}

0 commit comments

Comments
 (0)