Skip to content

Commit a6b0afd

Browse files
committed
add request to getToken param, allow custom hooks around getTokens
1 parent 6e4b234 commit a6b0afd

File tree

9 files changed

+152
-204
lines changed

9 files changed

+152
-204
lines changed

README.md

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,10 @@
2121

2222
Technically this plugin should work with all generic OAuth2 providers. Here are the list of providers that have been tested:
2323

24-
| Provider | Status | Example |
25-
| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------ |
26-
| Google | [![Test Google OAuth](https://github.com/WilsonLe/payload-oauth2/actions/workflows/test-google-oauth.yml/badge.svg)](https://github.com/WilsonLe/payload-oauth2/actions/workflows/test-google-oauth.yml) | [Config](./examples/google.md) |
27-
| Zitadel | [![Test Zitadel OAuth](https://github.com/WilsonLe/payload-oauth2/actions/workflows/test-zitadel-oauth.yml/badge.svg)](https://github.com/WilsonLe/payload-oauth2/actions/workflows/test-zitadel-oauth.yml) | [Config](./examples/zitadel.md) |
24+
| Provider | Status | Example |
25+
| -------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | ------------------------------- |
26+
| Google | [![Test Google OAuth](https://github.com/WilsonLe/payload-oauth2/actions/workflows/test-google-oauth.yml/badge.svg)](https://github.com/WilsonLe/payload-oauth2/actions/workflows/test-google-oauth.yml) | [Config](./examples/google.ts) |
27+
| Zitadel | [![Test Zitadel OAuth](https://github.com/WilsonLe/payload-oauth2/actions/workflows/test-zitadel-oauth.yml/badge.svg)](https://github.com/WilsonLe/payload-oauth2/actions/workflows/test-zitadel-oauth.yml) | [Config](./examples/zitadel.ts) |
2828

2929
# Installation
3030

dev/src/payload.config.ts

Lines changed: 3 additions & 86 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,8 @@ import path from "path";
44
import { buildConfig } from "payload";
55
import sharp from "sharp";
66
import { fileURLToPath } from "url";
7-
import { OAuth2Plugin } from "../../src/plugin";
7+
import { googleOAuth } from "../../examples/google";
8+
import { zitadelOAuth } from "../../examples/zitadel";
89
import Users from "./collections/Users";
910
import { migrations } from "./migrations";
1011

@@ -33,90 +34,6 @@ export default buildConfig({
3334
editor: lexicalEditor({}),
3435
collections: [Users],
3536
typescript: { outputFile: path.resolve(dirname, "payload-types.ts") },
36-
plugins: [
37-
////////////////////////////////////////////////////////////////////////////
38-
// Google OAuth
39-
////////////////////////////////////////////////////////////////////////////
40-
OAuth2Plugin({
41-
enabled:
42-
typeof process.env.GOOGLE_CLIENT_ID === "string" &&
43-
typeof process.env.GOOGLE_CLIENT_SECRET === "string",
44-
strategyName: "google",
45-
useEmailAsIdentity: true,
46-
serverURL: process.env.NEXT_PUBLIC_URL || "http://localhost:3000",
47-
clientId: process.env.GOOGLE_CLIENT_ID || "",
48-
clientSecret: process.env.GOOGLE_CLIENT_SECRET || "",
49-
authorizePath: "/oauth/google",
50-
callbackPath: "/oauth/google/callback",
51-
authCollection: "users",
52-
tokenEndpoint: "https://oauth2.googleapis.com/token",
53-
scopes: [
54-
"openid",
55-
"https://www.googleapis.com/auth/userinfo.email",
56-
"https://www.googleapis.com/auth/userinfo.profile",
57-
],
58-
providerAuthorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
59-
getUserInfo: async (accessToken: string) => {
60-
const response = await fetch(
61-
"https://www.googleapis.com/oauth2/v3/userinfo",
62-
{ headers: { Authorization: `Bearer ${accessToken}` } },
63-
);
64-
const user = await response.json();
65-
return { email: user.email, sub: user.sub };
66-
},
67-
successRedirect: (req) => {
68-
return "/admin";
69-
},
70-
failureRedirect: (req, err) => {
71-
req.payload.logger.error(err);
72-
return "/admin/login";
73-
},
74-
}),
75-
////////////////////////////////////////////////////////////////////////////
76-
// Zitadel OAuth
77-
////////////////////////////////////////////////////////////////////////////
78-
OAuth2Plugin({
79-
enabled:
80-
typeof process.env.ZITADEL_CLIENT_ID === "string" &&
81-
typeof process.env.ZITADEL_CLIENT_SECRET === "string" &&
82-
typeof process.env.ZITADEL_TOKEN_ENDPOINT === "string" &&
83-
typeof process.env.ZITADEL_AUTHORIZATION_URL === "string" &&
84-
typeof process.env.ZITADEL_USERINFO_ENDPOINT === "string",
85-
strategyName: "zitadel",
86-
useEmailAsIdentity: true,
87-
serverURL: process.env.NEXT_PUBLIC_URL || "http://localhost:3000",
88-
clientId: process.env.ZITADEL_CLIENT_ID || "",
89-
clientSecret: process.env.ZITADEL_CLIENT_SECRET || "",
90-
authorizePath: "/oauth/zitadel",
91-
callbackPath: "/oauth/zitadel/callback",
92-
authCollection: "users",
93-
tokenEndpoint: process.env.ZITADEL_TOKEN_ENDPOINT || "",
94-
scopes: [
95-
"openid",
96-
"profile",
97-
"email",
98-
"offline_access",
99-
"urn:zitadel:iam:user:metadata",
100-
],
101-
providerAuthorizationUrl: process.env.ZITADEL_AUTHORIZATION_URL || "",
102-
getUserInfo: async (accessToken: string) => {
103-
const response = await fetch(
104-
process.env.ZITADEL_USERINFO_ENDPOINT || "",
105-
{
106-
headers: { Authorization: `Bearer ${accessToken}` },
107-
},
108-
);
109-
const user = await response.json();
110-
return { email: user.email, sub: user.sub };
111-
},
112-
successRedirect: (req) => {
113-
return "/admin";
114-
},
115-
failureRedirect: (req, err) => {
116-
req.payload.logger.error(err);
117-
return "/admin/login";
118-
},
119-
}),
120-
],
37+
plugins: [googleOAuth, zitadelOAuth],
12138
sharp,
12239
});

examples/google.md

Lines changed: 0 additions & 50 deletions
This file was deleted.

examples/google.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { PayloadRequest } from "payload";
2+
import { OAuth2Plugin, defaultGetToken } from "../src/index";
3+
4+
////////////////////////////////////////////////////////////////////////////////
5+
// Google OAuth
6+
////////////////////////////////////////////////////////////////////////////////
7+
export const googleOAuth = OAuth2Plugin({
8+
enabled:
9+
typeof process.env.GOOGLE_CLIENT_ID === "string" &&
10+
typeof process.env.GOOGLE_CLIENT_SECRET === "string",
11+
strategyName: "google",
12+
useEmailAsIdentity: true,
13+
serverURL: process.env.NEXT_PUBLIC_URL || "http://localhost:3000",
14+
clientId: process.env.GOOGLE_CLIENT_ID || "",
15+
clientSecret: process.env.GOOGLE_CLIENT_SECRET || "",
16+
authorizePath: "/oauth/google",
17+
callbackPath: "/oauth/google/callback",
18+
authCollection: "users",
19+
tokenEndpoint: "https://oauth2.googleapis.com/token",
20+
scopes: [
21+
"openid",
22+
"https://www.googleapis.com/auth/userinfo.email",
23+
"https://www.googleapis.com/auth/userinfo.profile",
24+
],
25+
providerAuthorizationUrl: "https://accounts.google.com/o/oauth2/v2/auth",
26+
getUserInfo: async (accessToken: string) => {
27+
const response = await fetch(
28+
"https://www.googleapis.com/oauth2/v3/userinfo",
29+
{ headers: { Authorization: `Bearer ${accessToken}` } },
30+
);
31+
const user = await response.json();
32+
return { email: user.email, sub: user.sub };
33+
},
34+
/**
35+
* This param is complete optional to demonstrate customize your own
36+
* `getToken` function (i.e. add hooks to run after getting the token)
37+
* Leave this blank should you wish to use the default getToken function
38+
*/
39+
getToken: async (code: string, req: PayloadRequest) => {
40+
const redirectUri = `${process.env.NEXT_PUBLIC_URL || "http://localhost:3000"}/api/users/oauth/google/callback`;
41+
const token = await defaultGetToken(
42+
"https://oauth2.googleapis.com/token",
43+
process.env.GOOGLE_CLIENT_ID || "",
44+
process.env.GOOGLE_CLIENT_SECRET || "",
45+
redirectUri,
46+
code,
47+
);
48+
////////////////////////////////////////////////////////////////////////////
49+
// Consider this section afterToken hook
50+
////////////////////////////////////////////////////////////////////////////
51+
req.payload.logger.info("Received token: ${token} 👀");
52+
if (req.user) {
53+
req.payload.update({
54+
collection: "users",
55+
id: req.user.id,
56+
data: {},
57+
});
58+
}
59+
60+
return token;
61+
},
62+
successRedirect: (req) => {
63+
return "/admin";
64+
},
65+
failureRedirect: (req, err) => {
66+
req.payload.logger.error(err);
67+
return "/admin/login";
68+
},
69+
});

examples/zitadel.md

Lines changed: 0 additions & 62 deletions
This file was deleted.

examples/zitadel.ts

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import { PayloadRequest } from "payload";
2+
import { OAuth2Plugin, defaultGetToken } from "../src/index";
3+
4+
////////////////////////////////////////////////////////////////////////////////
5+
// Zitadel OAuth
6+
////////////////////////////////////////////////////////////////////////////////
7+
export const zitadelOAuth = OAuth2Plugin({
8+
enabled:
9+
typeof process.env.ZITADEL_CLIENT_ID === "string" &&
10+
typeof process.env.ZITADEL_CLIENT_SECRET === "string" &&
11+
typeof process.env.ZITADEL_TOKEN_ENDPOINT === "string" &&
12+
typeof process.env.ZITADEL_AUTHORIZATION_URL === "string" &&
13+
typeof process.env.ZITADEL_USERINFO_ENDPOINT === "string",
14+
strategyName: "zitadel",
15+
useEmailAsIdentity: true,
16+
serverURL: process.env.NEXT_PUBLIC_URL || "http://localhost:3000",
17+
clientId: process.env.ZITADEL_CLIENT_ID || "",
18+
clientSecret: process.env.ZITADEL_CLIENT_SECRET || "",
19+
authorizePath: "/oauth/zitadel",
20+
callbackPath: "/oauth/zitadel/callback",
21+
authCollection: "users",
22+
tokenEndpoint: process.env.ZITADEL_TOKEN_ENDPOINT || "",
23+
scopes: [
24+
"openid",
25+
"profile",
26+
"email",
27+
"offline_access",
28+
"urn:zitadel:iam:user:metadata",
29+
],
30+
providerAuthorizationUrl: process.env.ZITADEL_AUTHORIZATION_URL || "",
31+
getUserInfo: async (accessToken: string) => {
32+
const response = await fetch(process.env.ZITADEL_USERINFO_ENDPOINT || "", {
33+
headers: { Authorization: `Bearer ${accessToken}` },
34+
});
35+
const user = await response.json();
36+
return { email: user.email, sub: user.sub };
37+
},
38+
/**
39+
* This param is complete optional to demonstrate customize your own
40+
* `getToken` function (i.e. add hooks to run after getting the token)
41+
* Leave this blank should you wish to use the default getToken function
42+
*/
43+
getToken: async (code: string, req: PayloadRequest) => {
44+
const redirectUri = `${process.env.NEXT_PUBLIC_URL || "http://localhost:3000"}/api/users/oauth/zitadel/callback`;
45+
const token = await defaultGetToken(
46+
process.env.ZITADEL_TOKEN_ENDPOINT || "",
47+
process.env.ZITADEL_CLIENT_ID || "",
48+
process.env.ZITADEL_CLIENT_SECRET || "",
49+
redirectUri,
50+
code,
51+
);
52+
////////////////////////////////////////////////////////////////////////////
53+
// Consider this section afterToken hook
54+
////////////////////////////////////////////////////////////////////////////
55+
req.payload.logger.info("Received token: ${token} 👀");
56+
if (req.user) {
57+
req.payload.update({
58+
collection: "users",
59+
id: req.user.id,
60+
data: {},
61+
});
62+
}
63+
64+
return token;
65+
},
66+
successRedirect: (req) => {
67+
return "/admin";
68+
},
69+
failureRedirect: (req, err) => {
70+
req.payload.logger.error(err);
71+
return "/admin/login";
72+
},
73+
});

src/callback-endpoint.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -46,7 +46,7 @@ export const createCallbackEndpoint = (
4646
let access_token: string;
4747

4848
if (pluginOptions.getToken) {
49-
access_token = await pluginOptions.getToken(code);
49+
access_token = await pluginOptions.getToken(code, req);
5050
} else {
5151
access_token = await defaultGetToken(
5252
pluginOptions.tokenEndpoint,

src/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,3 @@
1+
export { defaultGetToken } from "./default-get-token";
12
export { OAuth2Plugin } from "./plugin";
23
export type { PluginTypes } from "./types";

src/types.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -119,7 +119,7 @@ export interface PluginTypes {
119119
*
120120
* Reference: `defaultGetToken` in `src/default-get-token.ts`
121121
*/
122-
getToken?: (code: string) => string | Promise<string>;
122+
getToken?: (code: string, req: PayloadRequest) => string | Promise<string>;
123123

124124
/**
125125
* Redirect users after successful login.

0 commit comments

Comments
 (0)