@webiny/mcp 6.0.0-rc.5 → 6.0.0-rc.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +4 -4
- package/skills/api-custom-feature/SKILL.md +195 -0
- package/skills/configure-auth0/SKILL.md +290 -0
- package/skills/configure-okta/SKILL.md +291 -0
- package/skills/custom-graphql-api/SKILL.md +145 -135
- package/skills/dependency-injection/SKILL.md +277 -149
- package/skills/infrastructure-extensions/SKILL.md +84 -66
- package/skills/lifecycle-events/SKILL.md +151 -6
- package/skills/local-development/SKILL.md +25 -16
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@webiny/mcp",
|
|
3
|
-
"version": "6.0.0-rc.
|
|
3
|
+
"version": "6.0.0-rc.6",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./index.js",
|
|
6
6
|
"repository": {
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"@modelcontextprotocol/sdk": "1.27.1",
|
|
19
|
-
"@webiny/cli-core": "6.0.0-rc.
|
|
19
|
+
"@webiny/cli-core": "6.0.0-rc.6",
|
|
20
20
|
"front-matter": "4.0.2",
|
|
21
21
|
"react": "18.2.0",
|
|
22
22
|
"zod": "3.25.76"
|
|
@@ -24,7 +24,7 @@
|
|
|
24
24
|
"devDependencies": {
|
|
25
25
|
"@types/lodash": "4.17.24",
|
|
26
26
|
"@types/ncp": "2.0.8",
|
|
27
|
-
"@webiny/build-tools": "6.0.0-rc.
|
|
27
|
+
"@webiny/build-tools": "6.0.0-rc.6",
|
|
28
28
|
"typescript": "5.9.3"
|
|
29
29
|
},
|
|
30
30
|
"scripts": {
|
|
@@ -45,5 +45,5 @@
|
|
|
45
45
|
]
|
|
46
46
|
}
|
|
47
47
|
},
|
|
48
|
-
"gitHead": "
|
|
48
|
+
"gitHead": "a2a076532809feabf674a6873464f09071d86c72"
|
|
49
49
|
}
|
|
@@ -0,0 +1,195 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: webiny-api-custom-feature
|
|
3
|
+
context: webiny-extensions
|
|
4
|
+
description: >
|
|
5
|
+
Creating custom API features with createFeature and createAbstraction.
|
|
6
|
+
Use this skill when the developer wants to define a new backend service,
|
|
7
|
+
register an abstraction in the DI container, create a reusable API module,
|
|
8
|
+
or wire up custom business logic as a Webiny feature. Covers the full pattern:
|
|
9
|
+
define an interface, create an abstraction token, implement and register
|
|
10
|
+
via createFeature, and register the extension in webiny.config.tsx.
|
|
11
|
+
---
|
|
12
|
+
|
|
13
|
+
# Custom API Features
|
|
14
|
+
|
|
15
|
+
## TL;DR
|
|
16
|
+
|
|
17
|
+
Use `createFeature` and `createAbstraction` from `"webiny/api"` to define reusable backend services. An abstraction is a DI token typed to an interface. A feature registers concrete implementations into the DI container. Other extensions (GraphQL schemas, lifecycle hooks, etc.) can then declare the abstraction as a dependency and receive the implementation automatically.
|
|
18
|
+
|
|
19
|
+
## Pattern
|
|
20
|
+
|
|
21
|
+
```typescript
|
|
22
|
+
// extensions/myFeature/MyFeature.ts
|
|
23
|
+
import { createFeature, createAbstraction } from "webiny/api";
|
|
24
|
+
|
|
25
|
+
// 1. Define the interface
|
|
26
|
+
export interface IMyService {
|
|
27
|
+
doSomething(): string;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// 2. Create the abstraction (DI token)
|
|
31
|
+
export const MyService = createAbstraction<IMyService>("MyService");
|
|
32
|
+
|
|
33
|
+
// 3. Export the interface via namespace (convention for consumers)
|
|
34
|
+
export namespace MyService {
|
|
35
|
+
export type Interface = IMyService;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
// 4. Register the feature (default export)
|
|
39
|
+
export default createFeature({
|
|
40
|
+
name: "MyApp/MyFeature",
|
|
41
|
+
register(container) {
|
|
42
|
+
// Register a plain object instance
|
|
43
|
+
container.registerInstance(MyService, {
|
|
44
|
+
doSomething() {
|
|
45
|
+
return "Hello!";
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
## Core APIs
|
|
53
|
+
|
|
54
|
+
### `createAbstraction<T>(name: string)`
|
|
55
|
+
|
|
56
|
+
Creates a typed DI token (an `Abstraction<T>` instance). The generic `T` is the interface that implementations must satisfy. The `name` string is used for debugging and error messages.
|
|
57
|
+
|
|
58
|
+
| Import | `import { createAbstraction } from "webiny/api"` |
|
|
59
|
+
|---|---|
|
|
60
|
+
| Returns | `Abstraction<T>` |
|
|
61
|
+
| Usage | Pass to `container.registerInstance()`, `container.register()`, or as a dependency in other extensions |
|
|
62
|
+
|
|
63
|
+
### `createFeature(def)`
|
|
64
|
+
|
|
65
|
+
Creates a feature definition that the framework loads as an API extension. Must be the **default export** of the file.
|
|
66
|
+
|
|
67
|
+
| Import | `import { createFeature } from "webiny/api"` |
|
|
68
|
+
|---|---|
|
|
69
|
+
| `def.name` | Unique feature name (convention: `"AppName/FeatureName"`) |
|
|
70
|
+
| `def.register(container)` | Called at startup with the DI `Container` instance |
|
|
71
|
+
|
|
72
|
+
### Container Registration Methods
|
|
73
|
+
|
|
74
|
+
| Method | When to Use |
|
|
75
|
+
|---|---|
|
|
76
|
+
| `container.registerInstance(abstraction, instance)` | Register a plain object that satisfies the interface |
|
|
77
|
+
| `container.register(Implementation)` | Register a class (created via `Abstraction.createImplementation`) |
|
|
78
|
+
| `container.registerFactory(abstraction, () => instance)` | Register a lazy factory |
|
|
79
|
+
|
|
80
|
+
## Extension Registration
|
|
81
|
+
|
|
82
|
+
Register the feature file using `<Api.Extension>` in your extension's React entry point:
|
|
83
|
+
|
|
84
|
+
```tsx
|
|
85
|
+
// extensions/myFeature/Extension.tsx
|
|
86
|
+
import React from "react";
|
|
87
|
+
import { Api } from "webiny/extensions";
|
|
88
|
+
|
|
89
|
+
export const MyFeature = () => {
|
|
90
|
+
return <Api.Extension src={"@/extensions/myFeature/MyFeature.ts"} />;
|
|
91
|
+
};
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
Then mount it in `webiny.config.tsx` or your app's extension composition.
|
|
95
|
+
|
|
96
|
+
## Examples
|
|
97
|
+
|
|
98
|
+
### Simple Service with Instance Registration
|
|
99
|
+
|
|
100
|
+
```typescript
|
|
101
|
+
// extensions/pricing/PricingFeature.ts
|
|
102
|
+
import { createFeature, createAbstraction } from "webiny/api";
|
|
103
|
+
|
|
104
|
+
export interface IPricingService {
|
|
105
|
+
calculatePrice(basePrice: number, quantity: number): number;
|
|
106
|
+
getDiscount(customerId: string): Promise<number>;
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
export const PricingService = createAbstraction<IPricingService>("PricingService");
|
|
110
|
+
|
|
111
|
+
export namespace PricingService {
|
|
112
|
+
export type Interface = IPricingService;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export default createFeature({
|
|
116
|
+
name: "MyApp/Pricing",
|
|
117
|
+
register(container) {
|
|
118
|
+
container.registerInstance(PricingService, {
|
|
119
|
+
calculatePrice(basePrice, quantity) {
|
|
120
|
+
return basePrice * quantity;
|
|
121
|
+
},
|
|
122
|
+
async getDiscount(_customerId) {
|
|
123
|
+
return 0.1; // 10% default discount
|
|
124
|
+
}
|
|
125
|
+
});
|
|
126
|
+
}
|
|
127
|
+
});
|
|
128
|
+
```
|
|
129
|
+
|
|
130
|
+
### Service with Class Registration and Dependencies
|
|
131
|
+
|
|
132
|
+
When your service itself needs dependencies, use `createImplementation` on the abstraction and `container.register()`:
|
|
133
|
+
|
|
134
|
+
```typescript
|
|
135
|
+
// extensions/notifications/NotificationFeature.ts
|
|
136
|
+
import { createFeature, createAbstraction } from "webiny/api";
|
|
137
|
+
import { Logger } from "webiny/api/logger";
|
|
138
|
+
|
|
139
|
+
export interface INotificationService {
|
|
140
|
+
notify(userId: string, message: string): Promise<void>;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export const NotificationService = createAbstraction<INotificationService>("NotificationService");
|
|
144
|
+
|
|
145
|
+
export namespace NotificationService {
|
|
146
|
+
export type Interface = INotificationService;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Class implementation with injected dependencies
|
|
150
|
+
class NotificationServiceImpl implements INotificationService {
|
|
151
|
+
constructor(private logger: Logger.Interface) {}
|
|
152
|
+
|
|
153
|
+
async notify(userId: string, message: string) {
|
|
154
|
+
this.logger.info(`Notifying ${userId}: ${message}`);
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
// Attach DI metadata to the class
|
|
159
|
+
const NotificationServiceRegistration = NotificationService.createImplementation({
|
|
160
|
+
implementation: NotificationServiceImpl,
|
|
161
|
+
dependencies: [Logger]
|
|
162
|
+
});
|
|
163
|
+
|
|
164
|
+
export default createFeature({
|
|
165
|
+
name: "MyApp/Notifications",
|
|
166
|
+
register(container) {
|
|
167
|
+
container.register(NotificationServiceRegistration);
|
|
168
|
+
}
|
|
169
|
+
});
|
|
170
|
+
```
|
|
171
|
+
|
|
172
|
+
## Key Rules
|
|
173
|
+
|
|
174
|
+
1. **Default export** -- `createFeature()` result must be the default export of the file.
|
|
175
|
+
2. **One feature per file** -- each `.ts` file loaded via `<Api.Extension>` should export one feature.
|
|
176
|
+
3. **Namespace convention** -- export `namespace MyService { export type Interface = IMyService; }` so consumers can type dependencies as `MyService.Interface`.
|
|
177
|
+
4. **Name uniqueness** -- feature names must be globally unique; use `"AppName/FeatureName"` convention.
|
|
178
|
+
5. **Constructor param order** -- when using class registration, the `dependencies` array must match constructor parameter order exactly.
|
|
179
|
+
|
|
180
|
+
## Quick Reference
|
|
181
|
+
|
|
182
|
+
| What | How |
|
|
183
|
+
|---|---|
|
|
184
|
+
| Import | `import { createFeature, createAbstraction } from "webiny/api"` |
|
|
185
|
+
| Create token | `const MyService = createAbstraction<IMyService>("MyService")` |
|
|
186
|
+
| Register instance | `container.registerInstance(MyService, { ... })` |
|
|
187
|
+
| Register class | `container.register(MyService.createImplementation({ implementation, dependencies }))` |
|
|
188
|
+
| Extension entry | `<Api.Extension src={"@/extensions/myFeature/MyFeature.ts"} />` |
|
|
189
|
+
| Deploy | `yarn webiny deploy api --env=dev` |
|
|
190
|
+
|
|
191
|
+
## Related Skills
|
|
192
|
+
|
|
193
|
+
- **webiny-dependency-injection** -- Full DI pattern details, all injectable services, decorator and composite patterns
|
|
194
|
+
- **webiny-custom-graphql-api** -- How to consume your custom feature's abstraction in a GraphQL schema
|
|
195
|
+
- **webiny-project-structure** -- Extension file layout and `webiny.config.tsx` registration
|
|
@@ -0,0 +1,290 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: webiny-configure-auth0
|
|
3
|
+
description: >
|
|
4
|
+
Configuring Auth0 as an identity provider (IDP) for Webiny projects.
|
|
5
|
+
Use this skill when the developer asks about Auth0 authentication, Auth0 SSO,
|
|
6
|
+
replacing Cognito with Auth0, setting up external identity providers, configuring
|
|
7
|
+
OIDC authentication, mapping JWT claims to Webiny identities, or customizing
|
|
8
|
+
the Auth0 login flow. Also relevant when asking about AUTH0_ISSUER, AUTH0_CLIENT_ID
|
|
9
|
+
environment variables, Auth0IdpConfig, or the MyAuth0Extension pattern.
|
|
10
|
+
---
|
|
11
|
+
|
|
12
|
+
# Configure Auth0 Authentication
|
|
13
|
+
|
|
14
|
+
## TL;DR
|
|
15
|
+
|
|
16
|
+
Webiny supports Auth0 as an external identity provider (IDP) to replace the default Cognito authentication. You create two files: an API config class that maps Auth0 JWT claims to Webiny identity data (`Auth0IdpConfig`), and a React extension component (`<Auth0 />`) that wires issuer URL, client ID, and the API config path. Register the extension in `webiny.config.tsx`, set two environment variables (`AUTH0_ISSUER`, `AUTH0_CLIENT_ID`), and deploy.
|
|
17
|
+
|
|
18
|
+
## Pattern / Core Concept
|
|
19
|
+
|
|
20
|
+
Auth0 integration has two parts:
|
|
21
|
+
|
|
22
|
+
1. **API Config** — A class implementing `Auth0IdpConfig.Interface` that maps JWT token claims to Webiny's identity structure. Registered via `Auth0IdpConfig.createImplementation()` (the universal DI pattern).
|
|
23
|
+
2. **Extension Component** — A React component that renders `<Auth0 />` from `@webiny/auth0`, passing the issuer URL, client ID, and path to the API config file. The `<Auth0 />` component handles environment variable injection, API extension registration, and Admin login screen setup automatically.
|
|
24
|
+
|
|
25
|
+
### How `<Auth0 />` Works Internally
|
|
26
|
+
|
|
27
|
+
The `<Auth0 />` component (from `@webiny/auth0`) is a `defineExtension` that:
|
|
28
|
+
|
|
29
|
+
- Sets Lambda env vars: `AUTH0_ISSUER`, `AUTH0_CLIENT_ID`
|
|
30
|
+
- Sets Admin app env vars: `REACT_APP_IDP_TYPE=auth0`, `REACT_APP_AUTH0_ISSUER`, `REACT_APP_AUTH0_CLIENT_ID`
|
|
31
|
+
- Registers the internal `Auth0IdpFeature` API extension (OIDC token verification)
|
|
32
|
+
- Registers your custom API config extension (identity mapping)
|
|
33
|
+
- Registers the Admin Auth0 login screen extension
|
|
34
|
+
|
|
35
|
+
## Reference Tables
|
|
36
|
+
|
|
37
|
+
### `Auth0IdpConfig.Interface`
|
|
38
|
+
|
|
39
|
+
| Method | Signature | Required | Description |
|
|
40
|
+
| ------------------- | ---------------------------------------------------------------- | -------- | ----------------------------------------------------- |
|
|
41
|
+
| `getIdentity` | `(token: JwtPayload) => Auth0Identity \| Promise<Auth0Identity>` | Yes | Maps JWT claims to Webiny identity data |
|
|
42
|
+
| `verifyTokenClaims` | `(token: JwtPayload) => void \| Promise<void>` | No | Custom claim verification (throw to reject the token) |
|
|
43
|
+
|
|
44
|
+
### `Auth0Identity` (Return Type of `getIdentity`)
|
|
45
|
+
|
|
46
|
+
| Field | Type | Description |
|
|
47
|
+
| ------------- | -------------------------------- | ------------------------------------------------ |
|
|
48
|
+
| `id` | `string` | Unique user ID (typically `token["sub"]`) |
|
|
49
|
+
| `displayName` | `string` | User's display name |
|
|
50
|
+
| `roles` | `string[]` | Webiny security roles to assign |
|
|
51
|
+
| `teams` | `string[]` | Webiny teams (optional, filter out falsy values) |
|
|
52
|
+
| `profile` | `{ firstName, lastName, email }` | User profile fields |
|
|
53
|
+
| `context` | `object` | Runtime data (not stored in DB) |
|
|
54
|
+
|
|
55
|
+
### `<Auth0 />` Component Props
|
|
56
|
+
|
|
57
|
+
| Prop | Type | Description |
|
|
58
|
+
| ----------- | -------- | -------------------------------------------------------- |
|
|
59
|
+
| `issuer` | `string` | Auth0 issuer URL (e.g., `https://your-tenant.auth0.com`) |
|
|
60
|
+
| `clientId` | `string` | Auth0 application client ID |
|
|
61
|
+
| `apiConfig` | `string` | Absolute path to the API config file |
|
|
62
|
+
|
|
63
|
+
### Environment Variables
|
|
64
|
+
|
|
65
|
+
| Variable | Used By | Description |
|
|
66
|
+
| ----------------- | ----------- | --------------------------- |
|
|
67
|
+
| `AUTH0_ISSUER` | API + Admin | Auth0 issuer URL |
|
|
68
|
+
| `AUTH0_CLIENT_ID` | API + Admin | Auth0 application client ID |
|
|
69
|
+
|
|
70
|
+
## Full Examples
|
|
71
|
+
|
|
72
|
+
### Example 1: Basic Auth0 Configuration
|
|
73
|
+
|
|
74
|
+
**Step 1: Create the API config**
|
|
75
|
+
|
|
76
|
+
Create `extensions/auth0/MyAuth0Config.ts`:
|
|
77
|
+
|
|
78
|
+
```typescript
|
|
79
|
+
import { Auth0IdpConfig } from "@webiny/auth0";
|
|
80
|
+
|
|
81
|
+
class MyIdpConfig implements Auth0IdpConfig.Interface {
|
|
82
|
+
getIdentity(token: Auth0IdpConfig.JwtPayload) {
|
|
83
|
+
return {
|
|
84
|
+
id: String(token["sub"]),
|
|
85
|
+
displayName: token["name"],
|
|
86
|
+
roles: ["full-access"],
|
|
87
|
+
profile: {
|
|
88
|
+
firstName: token["given_name"],
|
|
89
|
+
lastName: token["family_name"],
|
|
90
|
+
email: token["email"]
|
|
91
|
+
},
|
|
92
|
+
context: {
|
|
93
|
+
canAccessTenant: true,
|
|
94
|
+
defaultTenant: "root"
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const MyAuth0Config = Auth0IdpConfig.createImplementation({
|
|
101
|
+
implementation: MyIdpConfig,
|
|
102
|
+
dependencies: []
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
export default MyAuth0Config;
|
|
106
|
+
```
|
|
107
|
+
|
|
108
|
+
**Step 2: Create the extension component**
|
|
109
|
+
|
|
110
|
+
Create `extensions/auth0/MyAuth0Extension.tsx`:
|
|
111
|
+
|
|
112
|
+
```tsx
|
|
113
|
+
import React from "react";
|
|
114
|
+
import { Auth0 } from "@webiny/auth0";
|
|
115
|
+
|
|
116
|
+
export const MyAuth0Extension = () => {
|
|
117
|
+
return (
|
|
118
|
+
<Auth0
|
|
119
|
+
issuer={String(process.env.AUTH0_ISSUER)}
|
|
120
|
+
clientId={String(process.env.AUTH0_CLIENT_ID)}
|
|
121
|
+
apiConfig={import.meta.dirname + "/MyAuth0Config.ts"}
|
|
122
|
+
/>
|
|
123
|
+
);
|
|
124
|
+
};
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
**Step 3: Register in `webiny.config.tsx`**
|
|
128
|
+
|
|
129
|
+
```tsx
|
|
130
|
+
import React from "react";
|
|
131
|
+
import { MyAuth0Extension } from "./extensions/auth0/MyAuth0Extension.js";
|
|
132
|
+
|
|
133
|
+
export const Extensions = () => {
|
|
134
|
+
return (
|
|
135
|
+
<>
|
|
136
|
+
{/* Replace <Cognito /> with Auth0 */}
|
|
137
|
+
<MyAuth0Extension />
|
|
138
|
+
|
|
139
|
+
{/* ... other extensions ... */}
|
|
140
|
+
</>
|
|
141
|
+
);
|
|
142
|
+
};
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
**Step 4: Set environment variables**
|
|
146
|
+
|
|
147
|
+
Add to your `.env` file (or CI/CD environment):
|
|
148
|
+
|
|
149
|
+
```
|
|
150
|
+
AUTH0_ISSUER=https://your-tenant.auth0.com/
|
|
151
|
+
AUTH0_CLIENT_ID=your-auth0-client-id
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
**Step 5: Deploy**
|
|
155
|
+
|
|
156
|
+
```bash
|
|
157
|
+
yarn webiny deploy
|
|
158
|
+
```
|
|
159
|
+
|
|
160
|
+
### Example 2: Custom Claim Verification
|
|
161
|
+
|
|
162
|
+
If your Auth0 setup uses custom claims (e.g., via Auth0 Actions or Rules) that need validation:
|
|
163
|
+
|
|
164
|
+
```typescript
|
|
165
|
+
import { Auth0IdpConfig } from "@webiny/auth0";
|
|
166
|
+
|
|
167
|
+
class MyIdpConfig implements Auth0IdpConfig.Interface {
|
|
168
|
+
getIdentity(token: Auth0IdpConfig.JwtPayload) {
|
|
169
|
+
return {
|
|
170
|
+
id: String(token["sub"]),
|
|
171
|
+
displayName: token["name"],
|
|
172
|
+
roles: [token["https://webiny.com/role"]],
|
|
173
|
+
profile: {
|
|
174
|
+
firstName: token["given_name"],
|
|
175
|
+
lastName: token["family_name"],
|
|
176
|
+
email: token["email"]
|
|
177
|
+
},
|
|
178
|
+
context: {
|
|
179
|
+
canAccessTenant: true,
|
|
180
|
+
defaultTenant: "root"
|
|
181
|
+
}
|
|
182
|
+
};
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
verifyTokenClaims(token: Auth0IdpConfig.JwtPayload) {
|
|
186
|
+
// Reject tokens without the required custom claim
|
|
187
|
+
if (!token["https://webiny.com/role"]) {
|
|
188
|
+
throw new Error("Token is missing the 'https://webiny.com/role' claim.");
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
// Reject tokens from unauthorized organizations
|
|
192
|
+
if (token["org_id"] && token["org_id"] !== "org_expected") {
|
|
193
|
+
throw new Error("User does not belong to the authorized organization.");
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const MyAuth0Config = Auth0IdpConfig.createImplementation({
|
|
199
|
+
implementation: MyIdpConfig,
|
|
200
|
+
dependencies: []
|
|
201
|
+
});
|
|
202
|
+
|
|
203
|
+
export default MyAuth0Config;
|
|
204
|
+
```
|
|
205
|
+
|
|
206
|
+
### Example 3: Using DI Dependencies in Config
|
|
207
|
+
|
|
208
|
+
If your config needs access to other Webiny services (e.g., to look up tenant-specific roles):
|
|
209
|
+
|
|
210
|
+
```typescript
|
|
211
|
+
import { Auth0IdpConfig } from "@webiny/auth0";
|
|
212
|
+
import { TenantContext } from "webiny/api/tenancy";
|
|
213
|
+
|
|
214
|
+
class MyIdpConfig implements Auth0IdpConfig.Interface {
|
|
215
|
+
constructor(private tenantContext: TenantContext.Interface) {}
|
|
216
|
+
|
|
217
|
+
getIdentity(token: Auth0IdpConfig.JwtPayload) {
|
|
218
|
+
const tenant = this.tenantContext.getTenant();
|
|
219
|
+
|
|
220
|
+
return {
|
|
221
|
+
id: String(token["sub"]),
|
|
222
|
+
displayName: token["name"],
|
|
223
|
+
roles: [token["https://webiny.com/role"]],
|
|
224
|
+
profile: {
|
|
225
|
+
firstName: token["given_name"],
|
|
226
|
+
lastName: token["family_name"],
|
|
227
|
+
email: token["email"]
|
|
228
|
+
},
|
|
229
|
+
context: {
|
|
230
|
+
canAccessTenant: true,
|
|
231
|
+
defaultTenant: tenant?.id ?? "root"
|
|
232
|
+
}
|
|
233
|
+
};
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
const MyAuth0Config = Auth0IdpConfig.createImplementation({
|
|
238
|
+
implementation: MyIdpConfig,
|
|
239
|
+
dependencies: [TenantContext]
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
export default MyAuth0Config;
|
|
243
|
+
```
|
|
244
|
+
|
|
245
|
+
## Quick Reference
|
|
246
|
+
|
|
247
|
+
### Imports
|
|
248
|
+
|
|
249
|
+
```typescript
|
|
250
|
+
// API config
|
|
251
|
+
import { Auth0IdpConfig } from "@webiny/auth0";
|
|
252
|
+
|
|
253
|
+
// Extension component
|
|
254
|
+
import { Auth0 } from "@webiny/auth0";
|
|
255
|
+
```
|
|
256
|
+
|
|
257
|
+
### Key Interfaces
|
|
258
|
+
|
|
259
|
+
| Interface | Package | Purpose |
|
|
260
|
+
| ----------------------------- | --------------- | -------------------------------- |
|
|
261
|
+
| `Auth0IdpConfig.Interface` | `@webiny/auth0` | API-side JWT-to-identity mapping |
|
|
262
|
+
| `Auth0IdpConfig.JwtPayload` | `@webiny/auth0` | JWT token payload type |
|
|
263
|
+
| `Auth0IdpConfig.IdentityData` | `@webiny/auth0` | Identity return type |
|
|
264
|
+
|
|
265
|
+
### File Structure
|
|
266
|
+
|
|
267
|
+
```
|
|
268
|
+
extensions/auth0/
|
|
269
|
+
├── MyAuth0Config.ts # API config (JWT claim mapping)
|
|
270
|
+
└── MyAuth0Extension.tsx # Extension component (Auth0 setup)
|
|
271
|
+
```
|
|
272
|
+
|
|
273
|
+
### Registration
|
|
274
|
+
|
|
275
|
+
In `webiny.config.tsx`, replace `<Cognito />` with `<MyAuth0Extension />`.
|
|
276
|
+
|
|
277
|
+
### Deploy
|
|
278
|
+
|
|
279
|
+
```bash
|
|
280
|
+
yarn webiny deploy # Deploy all (Core + API + Admin)
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
Both API and Admin need to be redeployed since Auth0 affects both the backend (token verification, identity mapping) and the frontend (login screen).
|
|
284
|
+
|
|
285
|
+
## Related Skills
|
|
286
|
+
|
|
287
|
+
- **configure-okta** — Alternative IDP: configuring Okta authentication
|
|
288
|
+
- **dependency-injection** — The universal DI pattern used by `Auth0IdpConfig.createImplementation()`
|
|
289
|
+
- **project-structure** — How `webiny.config.tsx` and extensions are organized
|
|
290
|
+
- **local-development** — Deploying and testing your Auth0 configuration
|