Skip to content

Commit b0d42ad

Browse files
Merge pull request #403 from kinde-oss/Feat/wf-doc-pic
Workflow examples / testing and observability sections
2 parents dc281bc + 5560911 commit b0d42ad

18 files changed

+464
-518
lines changed

src/content/docs/workflows/example-workflows/existing-password-provided-workflow.mdx

Lines changed: 38 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -17,95 +17,76 @@ This trigger fires after an existing password is entered in the sign-in flow.
1717
Security is at the heart of our technical decisions at Kinde, and keeping user passwords safe is a huge part of this. Therefore:
1818

1919
- Any attempt to log the password out to the console in this workflow will be redacted
20-
- API calls can only be made from these workflows using the Kinde provided `secureFetch` method which secures the payload with an encryption key
20+
- API calls should only be made from these workflows using the Kinde provided [secureFetch](/workflows/bindings/secure-fetch-binding/) binding which secures the payload with an encryption key
2121

2222
## Example use cases
2323

2424
### Drip feed migration
2525

26-
For gradual migrations to Kinde where you wish to check the password against an external database before creating the user in Kinde.
26+
For gradual migrations to Kinde where you wish to check the password against an external database before creating the user in Kinde and migrating their password. [See example code](https://github.com/kinde-starter-kits/workflow-examples/blob/main/existingPassword/dripFeedMigrationWorkflow.ts)
2727

2828
## Workflow code
2929

30-
### The event object
30+
### Sample event object
3131

3232
The main argument provided to your code is the Kinde workflow `event` object which has two keys `request` and `context`. This gives you access to the reason the workflow was triggered. Here's an example:
3333

34-
```jsx
34+
```json
3535
{
36-
"request": {},
37-
"context": {
38-
"domains": {
39-
"kindeDomain": "https://example.kinde.com" // Your Kinde domain
40-
},
41-
"auth": {
42-
"provided email": x_provided_email, // the email provided by the user
43-
"password":"someSecurePassword", // the raw password
44-
"hashedPassword": "someHash", // the hashed password,
45-
"hasUserRecordInKinde": false // whether the user exists already in Kinde
46-
},
47-
"user": {
48-
"id": "kp_1234566" // only provided in password reset flows as otherwise new user
49-
}
50-
}
51-
```
52-
53-
### Workflow settings
54-
55-
```jsx
56-
export const workflowSettings = {
57-
id: "verifyPassword",
58-
name: "Verify password",
59-
failurePolicy: {action: "stop"},
60-
trigger: "user:existing_password_provided",
61-
bindings: {
62-
"kinde.secureFetch": {}, // Required for external API calls
63-
"kinde.widget": {} // Required to invalidate the form
36+
"request": {
37+
"ip": "192.168.0.1",
38+
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:135.0) Gecko/20100101 Firefox/135.0"
39+
},
40+
"context": {
41+
"domains": {
42+
"kindeDomain": "https://example.kinde.com" // Your Kinde domain
43+
},
44+
"auth": {
45+
"providedEmail": "[email protected]", // the email provided by the user
46+
"password": "someSecurePassword", // the raw password
47+
"hashedPassword": "someHash", // the hashed password,
48+
"hasUserRecordInKinde": false // whether the user exists already in Kinde
49+
},
50+
"user": {
51+
"id": "kp_1234566" // only provided in password reset flows as otherwise new user
52+
},
53+
"workflow": {
54+
"trigger": "user:existing_password_provided"
55+
}
6456
}
65-
};
57+
}
6658
```
6759

6860
### Secure fetch binding
6961

70-
When an API call is made using `kinde.secureFetch()` the body is automatically encrypted with the active encryption key for the workflow. This can be generated under **Workflow > Encryption keys**.
71-
72-
You will need to use the same encryption key in your own code to decrypt the payload on arrival. This ensures secure transfer of the password.
73-
74-
We handle the encryption for you so your code might look like:
75-
76-
```jsx
77-
const response = await kinde.secureFetch(`<YOUR_EXTERNAL_PASSWORD_DATABASE_ENDPOINT`, {
78-
method: 'POST',
79-
responseFormat: 'json',
80-
headers: {
81-
'content-type': 'application/json'
82-
body: {
83-
email: event.context.auth.providedEmail,
84-
password: event.context.auth.password
85-
}
86-
});
87-
```
62+
We recommend you use the [secureFetch](/workflows/bindings/secure-fetch-binding/) binding to make API calls from your workflow if they include sensitive data like passwords.
8863

8964
### Widget binding
9065

9166
The `kinde.widget` binding gives you access to the Kinde widget, which is the central form on the page. In this case the form with the two password fields.
9267

9368
It exposes a method for invalidating a form field `invalidateFormField`
9469

95-
```jsx
70+
```js
9671
kinde.widget.invalidateFormField(fieldName, message);
9772
```
9873

9974
Example
10075

101-
```jsx
102-
const isMinCharacters = context.auth.Password.length >= 50;
103-
104-
kinde.widget.invalidateFormField("p_password", "Nope");
76+
```js
77+
if (!isUserPasswordValid) {
78+
kinde.widget.invalidateFormField("p_password", "User or password not found");
79+
}
10580
```
10681

107-
The field names for this workflow are
82+
The field names for the widget binding in this workflow are:
10883

10984
| Field name | Description |
11085
| ------------ | ------------------ |
11186
| `p_password` | The password field |
87+
88+
### Example workflows
89+
90+
See examples on GitHub:
91+
92+
[Drip feed migration](https://github.com/kinde-starter-kits/workflow-examples/blob/main/existingPassword/dripFeedMigrationWorkflow.ts) - Shows how to check a password against an external database before creating the user in Kinde.

src/content/docs/workflows/example-workflows/m2m-token-generation-workflow.mdx

Lines changed: 27 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -26,103 +26,43 @@ You may want to add additional custom claims to the M2M token before it is deliv
2626

2727
### Correlate an M2M application with an organization or user
2828

29-
If you want, you can use M2M applications similar to API keys to enable access to various endpoints and tie them to an organization or user. For example, you add the organization code as a [custom property](/properties/work-with-properties/manage-properties/) on the M2M application, then fetch any data you’d like to include in the token.
29+
If you want, you can use M2M applications similar to API keys to enable access to various endpoints and tie them to an organization or user. For example, you add the organization code as a [custom property](/properties/work-with-properties/manage-properties/) on the M2M application, then fetch any data you’d like to include in the token. [See example code](https://github.com/kinde-starter-kits/workflow-examples/blob/main/m2mToken/mapOrgToM2MApplicationWorkflow.ts)
3030

3131
## Workflow code
3232

33-
### The event object
34-
35-
The main argument provided to your code is the Kinde workflow `event` object which has two keys `request` and `context`. This gives you access to the reason the workflow was triggered. Here's an example:
36-
37-
````jsx
38-
{
39-
"request": {
40-
"auth": {
41-
"audience": ["<EXAMPLE_API>"]
42-
},
43-
"ip": "192.168.0.1"
44-
},
45-
"context": {
46-
"domains": {
47-
"kindeDomain": "https://example.kinde.com" // Your Kinde domain
48-
},
49-
"application": {
50-
"clientId": "299627bd8bfa493f8b17e6aec8ebfb86" // the M2M application ID
51-
},
52-
"workflow": {
53-
"trigger": "m2m:token_generation"
54-
}
55-
}
56-
57-
### Workflow settings
58-
59-
```jsx
60-
export const workflowSettings = {
61-
id: "m2mTokenGeneration",
62-
name: "M2M custom claims",
63-
failurePolicy: {
64-
action: "stop",
65-
},
66-
trigger: "m2m:token_generation",
67-
bindings: {
68-
"kinde.m2mToken": {}, // required to modify M2M access token
69-
"kinde.fetch": {}, // Required for external API calls
70-
"kinde.env": {}, // required to access your environment variables
71-
url: {}, // required for url params
72-
},
73-
};
74-
````
75-
7633
### M2M token binding
7734

78-
The `kinde.m2mToken` binding is used to modify claims in the generated access token.
35+
The [kinde.m2mToken](/workflows/bindings/m2m-token-binding/) binding is used to modify claims in the generated access token.
7936

80-
### A simple example
37+
### Sample event object
8138

82-
```jsx
83-
kinde.m2mToken.setCustomClaim("hello", "world");
84-
```
85-
86-
### An advanced example using Kinde API to correlate an organization to an M2M application.
87-
88-
```jsx
89-
import {createKindeAPI} from "@kinde/infrastructure";
39+
The main argument provided to your code is the Kinde workflow `event` object which has two keys `request` and `context`. This gives you access to the reason the workflow was triggered. Here's an example:
9040

91-
export const workflowSettings = {
92-
id: "m2mTokenGeneration",
93-
name: "M2M custom claims",
94-
failurePolicy: {
95-
action: "stop"
41+
```json
42+
{
43+
"request": {
44+
"auth": {
45+
"audience": ["<EXAMPLE_API>"],
46+
"scope": ["read:users"]
47+
},
48+
"ip": "192.168.0.1"
9649
},
97-
trigger: "m2m:token_generation",
98-
bindings: {
99-
"kinde.m2mToken": {}, // required to modify M2M access token
100-
"kinde.fetch": {}, // Required for external API calls
101-
"kinde.env": {}, // required to access your environment variables
102-
url: {} // required for url params
50+
"context": {
51+
"domains": {
52+
"kindeDomain": "https://example.kinde.com" // Your Kinde domain
53+
},
54+
"application": {
55+
"clientId": "299627bd8bfa493f8b17e6aec8ebfb86" // the M2M application ID
56+
},
57+
"workflow": {
58+
"trigger": "m2m:token_generation"
59+
}
10360
}
104-
};
105-
106-
export default async function handleM2M(event) {
107-
// Get a token for Kinde management API
108-
const kindeAPI = await createKindeAPI(event);
109-
110-
// Call Kinde applications properties API
111-
const {data} = await kindeAPI.get({
112-
endpoint: `applications/${event.context.application.clientId}/properties`
113-
});
114-
const {appProperties} = data;
61+
}
62+
```
11563

116-
// Get the org code property to make the correlation
117-
const orgCode = appProperties.find((prop) => prop.key === "org_code");
64+
### Example workflows
11865

119-
// Get org data from Kinde management API
120-
const {data: org} = await kindeAPI.get({
121-
endpoint: `organization?code=${orgCode.value}`
122-
});
66+
See examples on GitHub:
12367

124-
// Use the data to set the org data on the M2M token
125-
kinde.m2mToken.setCustomClaim("orgName", org.name);
126-
kinde.m2mToken.setCustomClaim("orgCode", org.code);
127-
}
128-
```
68+
[Map M2M applications to organizations](https://github.com/kinde-starter-kits/workflow-examples/blob/main/m2mToken/mapOrgToM2MApplicationWorkflow.ts) - Shows how to map M2M applications to organizations. Useful if using Kinde for B2B API key management

src/content/docs/workflows/example-workflows/new-password-provided-workflow.mdx

Lines changed: 39 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ This trigger fires after a new password is entered in either the sign-up flow or
1717
Security is at the heart of our technical decisions at Kinde, and keeping user passwords safe is a huge part of this. Therefore:
1818

1919
- Any attempt to log the password out to the console in this workflow will be redacted
20-
- API calls can only be made from these workflows using the Kinde provided `secureFetch` method which secures the payload with an encryption key
20+
- API calls should only be made from these workflows using the Kinde provided [secureFetch](/workflows/bindings/secure-fetch-binding/) binding which secures the payload with an encryption key
2121

2222
## Example use cases
2323

@@ -29,23 +29,26 @@ As a baseline, Kinde runs the following password checks:
2929
- The password is at least 8 characters long
3030
- The password does not exist in the 1,000,000 most common passwords
3131

32-
With this workflow you can add your own custom code to run additional checks, for example if your business requires a specific mix of upper / lower case characters, or inclusion of special characters.
32+
With this workflow you can add your own custom code to run additional checks, for example if your business requires a specific mix of upper / lower case characters, or inclusion of special characters. [See example code](https://github.com/kinde-starter-kits/workflow-examples/blob/main/existingPassword/customPasswordValidationWorkflow.ts)
3333

34-
### Sync password with an external system
34+
### Sync password updates with an external system
3535

3636
For gradual migrations to Kinde where several apps are in play (e.g. a mobile application and a web application), you might want to migrate web users first and mobile app users later. If users have access to both applications, then password resets on the web application would not be persisted to the legacy mobile app password store.
3737

38-
With this workflow you can securely send the password to your mobile app system in order to keep them in sync.
38+
With this workflow you can securely send the password to your mobile app system in order to keep them in sync. [See example code](https://github.com/kinde-starter-kits/workflow-examples/blob/main/newPassword/securelySyncPasswordWorkflow.ts)
3939

4040
## Workflow code
4141

42-
### The event object
42+
### Sample event object
4343

4444
The main argument provided to your code is the Kinde workflow `event` object which has two keys `request` and `context`. This gives you access to the reason the workflow was triggered. Here's an example:
4545

46-
```jsx
46+
```json
4747
{
48-
"request": {},
48+
"request": {
49+
"ip": "192.168.0.1",
50+
"userAgent": "Mozilla/5.0 (Macintosh; Intel Mac OS X 10.15; rv:135.0) Gecko/20100101 Firefox/135.0"
51+
},
4952
"context": {
5053
"domains": {
5154
"kindeDomain": "https://example.kinde.com" // Your Kinde domain
@@ -54,63 +57,59 @@ The main argument provided to your code is the Kinde workflow `event` object whi
5457
"firstPassword": "someSecurePassword", // the first password entered
5558
"secondPassword": "someSecurePassword", // password match field
5659
"newPasswordReason": "reset" | "initial" // whether it is registration or reset
57-
}
60+
},
5861
"user": {
59-
"id": "kp_1234566" // only provided in password reset flows as otherwise new user
62+
"id": "kp_1234566", // only provided in password reset flows as otherwise new user
6063
"email": "[email protected]" // the email provided
64+
},
65+
"workflow": {
66+
"trigger": "user:new_password_provided"
6167
}
68+
}
6269
}
6370
```
6471

65-
### Workflow settings
66-
67-
```jsx
68-
export const workflowSettings = {
69-
id: "passwordReset",
70-
name: "Reset password",
71-
failurePolicy: {
72-
action: "stop"
73-
},
74-
trigger: "user:new_password_provided",
75-
bindings: {
76-
"kinde.secureFetch": {}, // Required for external API calls
77-
"kinde.widget": {} // Required to invalidate the form
78-
}
79-
};
80-
```
72+
## Bindings
8173

8274
### Secure fetch binding
8375

84-
When an API call is made using `kinde.secureFetch()` the body is automatically encrypted with the active encryption key for the workflow. This can be generated under **Workflow > Encryption keys**.
85-
86-
You will need to use the same encryption key in your own code to decrypt the payload on arrival. This ensures secure transfer of the password.
87-
88-
We handle the encryption for you so your code might look like:
76+
We recommend you use the [secureFetch](/workflows/bindings/secure-fetch-binding/) binding to make API calls from your workflow if they include sensitive data like passwords.
8977

9078
### Widget binding
9179

9280
The `kinde.widget` binding gives you access to the Kinde widget which is the central form on the page. In this case the form with the two password fields.
9381

9482
It exposes a method for invalidating a form field `invalidateFormField`.
9583

96-
```jsx
84+
```js
9785
kinde.widget.invalidateFormField(fieldName, message);
9886
```
9987

10088
Example
10189

102-
```jsx
90+
```js
10391
const isMinCharacters = context.auth.firstPassword.length >= 50;
10492

105-
kinde.widget.invalidateFormField(
106-
"p_first_password",
107-
"Provide a password at least 50 characters long"
108-
);
93+
if (!isMinCharacters) {
94+
kinde.widget.invalidateFormField(
95+
"p_first_password",
96+
"Provide a password at least 50 characters long"
97+
);
98+
}
99+
109100
```
110101

111-
The field names for this workflow are
102+
The field names for the widget binding in this workflow are:
112103

113-
| Field name | Description |
114-
| -------------- | ---------------------------------------------------------------------------------------- |
115-
| `p_first_password` | The first password field |
104+
| Field name | Description |
105+
| ------------------- | ---------------------------------------------------------------------------------------- |
106+
| `p_first_password` | The first password field |
116107
| `p_second_password` | The second password field, typically to check it matches the first to help prevent typos |
108+
109+
### Example workflows
110+
111+
See examples on GitHub:
112+
113+
- [Sync passwords to another system](https://github.com/kinde-starter-kits/workflow-examples/blob/main/newPassword/securelySyncPasswordWorkflow.ts) - Use encryption keys to securely keep passwords in sync between systems.
114+
- [Custom password validation](https://github.com/kinde-starter-kits/workflow-examples/blob/main/newPassword/customPasswordValidationWorkflow.ts) - Shows how to validate a password against your own rules.
115+

0 commit comments

Comments
 (0)