@lwrjs/auth-middleware 0.10.0-alpha.2 → 0.10.0-alpha.21

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/README.md CHANGED
@@ -28,7 +28,7 @@ platformWebServerAuthMiddleware(lwrApp.getServer()); // Pass in the generic LWR
28
28
  Start the LWR server, and pass the [Connected App information](#variables) in using environment variables:
29
29
 
30
30
  ```
31
- MY_DOMAIN=https://somewhere.force.com CLIENT_KEY=abc.123 CLIENT_SECRET=****** yarn start
31
+ CLIENT_KEY=abc.123 CLIENT_SECRET=****** yarn start
32
32
  ```
33
33
 
34
34
  Authenticate by accessing the `/login` [endpoint](#endpoints) from the LWR app:
@@ -91,7 +91,6 @@ platformWebServerAuthMiddleware(lwrApp.getServer(), { proxyEndpoint: '/some/wher
91
91
 
92
92
  In order to authenticate with the Web Server Flow, the following Connected App information is needed:
93
93
 
94
- - `MY_DOMAIN`: The [domain of the Salesforce org](https://help.salesforce.com/s/articleView?id=sf.faq_domain_name_what.htm&type=5) (_Note_: Find this in Setup -> "My Domain"; this is **not** necessarily the same as the URL in the browser address bar when visiting the org).
95
94
  - `CLIENT_KEY`: The public OAuth identifier for the Connected App (i.e. "Consumer Key", "Client ID").
96
95
  - `CLIENT_SECRET`: The password to go along with the `CLIENT_KEY` (i.e. "Consumer Secret", "Client Secret"), known only to the Connected App the and LWR server.
97
96
 
@@ -99,6 +98,12 @@ This information is passed to the LWR server via environment variables to keep t
99
98
 
100
99
  > Read more about the OAuth Client ID and Secret [here](https://www.oauth.com/oauth2-servers/client-registration/client-id-secret/).
101
100
 
101
+ #### Optional Variables
102
+
103
+ Additionally, the following optional environment variables are supported:
104
+
105
+ - `MY_DOMAIN`: The [domain of the Salesforce org](https://help.salesforce.com/s/articleView?id=sf.faq_domain_name_what.htm&type=5) (_Note_: Find this in Setup -> "My Domain"; this is **not** necessarily the same as the URL in the browser address bar when visiting the org). If this is not set then `https://login.salesforce.com` will be used as the fallback login server. NOTE: if a login request has the `login_server` query parameter set that overrides `MY_DOMAIN` for that login request.
106
+
102
107
  ### Endpoints
103
108
 
104
109
  The LWR authentication middleware provides the following endpoints:
@@ -106,6 +111,7 @@ The LWR authentication middleware provides the following endpoints:
106
111
  - `/login`: Accessed from the LWR app client code to trigger the OAuth flow and allow the current user to log in.
107
112
  - The `/login` endpoint takes an optional query parameter: `app_path`. Set `app_path` to the [path and parameters](https://developer.mozilla.org/en-US/docs/Learn/Common_questions/What_is_a_URL#path_to_resource) the LWR server should redirect to after authenticating (e.g. `/home`, `/list?sort=desc`). If omitted, `app_path` defaults to `/`. This value must be URL encoded.
108
113
  - `/oauth`: Used internally as part of the OAuth flow, this is the [OAuth redirect URI](https://www.oauth.com/oauth2-servers/redirect-uris/) for the [Connected App](#connected-app-setup) which fetches the OAuth token.
114
+ - `/revoke`: Accessed from the LWR app client code to trigger a revoke of the access token. This should be called whenever a user logs out of the LWR app client.
109
115
  - proxy endpoint: Requests sent to this endpoint are proxied to the Salesforce org with the OAuth token in an [Authorization header](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Authorization). By proxying these requests through the LWR server, the token remains secure from inspection by the client. The proxy endpoint is optionally passed into the [`platformWebServerAuthMiddleware`](#middleware) as an option; the default proxy endpoint is `/services/data`.
110
116
 
111
117
  ### Flow
@@ -9,25 +9,28 @@ var __export = (target, all) => {
9
9
  __markAsModule(exports);
10
10
  __export(exports, {
11
11
  COOKIE_NAME: () => COOKIE_NAME,
12
+ DEFAULT_LOGIN_SERVER: () => DEFAULT_LOGIN_SERVER,
12
13
  LOGIN_ENDPOINT: () => LOGIN_ENDPOINT,
13
14
  MESSAGE_PREFIX: () => MESSAGE_PREFIX,
14
15
  PROXY_ENDPOINT: () => PROXY_ENDPOINT,
15
16
  REDIRECT_ENDPOINT: () => REDIRECT_ENDPOINT,
17
+ REVOKE_ENDPOINT: () => REVOKE_ENDPOINT,
16
18
  buildProxyRequest: () => buildProxyRequest,
17
19
  buildRedirectUrl: () => buildRedirectUrl,
18
20
  generateStateString: () => generateStateString,
19
- getAppPath: () => getAppPath,
20
21
  getOauthInfoFromCookie: () => getOauthInfoFromCookie,
21
- getPlatformWebServerEnvVars: () => getPlatformWebServerEnvVars
22
+ getPlatformWebServerEnvVars: () => getPlatformWebServerEnvVars,
23
+ sanitizeAppPath: () => sanitizeAppPath
22
24
  });
23
25
  var MESSAGE_PREFIX = "Web Server OAuth Middleware - ";
24
26
  var LOGIN_ENDPOINT = "/login";
27
+ var REVOKE_ENDPOINT = "/revoke";
25
28
  var REDIRECT_ENDPOINT = "/oauth";
26
29
  var PROXY_ENDPOINT = "/services/data";
27
30
  var COOKIE_NAME = "lwr_web_server_oauth_info";
28
- var REDIRECT_QUERY = "app_path";
31
+ var DEFAULT_LOGIN_SERVER = "https://login.salesforce.com";
29
32
  function buildRedirectUrl(req) {
30
- return encodeURIComponent(`${req.protocol}://${req.get("host")}${REDIRECT_ENDPOINT}`);
33
+ return `${req.protocol}://${req.get("host")}${REDIRECT_ENDPOINT}`;
31
34
  }
32
35
  function getPlatformWebServerEnvVars() {
33
36
  const env = {myDomain: "MY_DOMAIN", clientKey: "CLIENT_KEY", clientSecret: "CLIENT_SECRET"};
@@ -35,9 +38,6 @@ function getPlatformWebServerEnvVars() {
35
38
  const clientKey = process.env[env.clientKey];
36
39
  const clientSecret = process.env[env.clientSecret];
37
40
  const errorMsg = "Web Server OAuth Middleware: missing process.env.";
38
- if (!myDomain) {
39
- throw new Error(`${errorMsg}${env.myDomain}`);
40
- }
41
41
  if (!clientKey) {
42
42
  throw new Error(`${errorMsg}${env.clientKey}`);
43
43
  }
@@ -49,13 +49,13 @@ function getPlatformWebServerEnvVars() {
49
49
  function generateStateString() {
50
50
  return `s${Math.floor(Math.random() * 65536).toString(16)}`;
51
51
  }
52
- function getAppPath(req) {
53
- let appPath = req.query[REDIRECT_QUERY];
54
- if (appPath && !decodeURIComponent(appPath).startsWith("/")) {
52
+ function sanitizeAppPath(app_path) {
53
+ let appPath = app_path ? decodeURIComponent(app_path) : `/`;
54
+ if (!appPath.startsWith("/")) {
55
55
  console.warn('The "app_path" parameter must be a relative path staring with "/". Defaulting to "app_path" = "/".');
56
- appPath = void 0;
56
+ appPath = "/";
57
57
  }
58
- return appPath ? decodeURIComponent(appPath) : "/";
58
+ return appPath;
59
59
  }
60
60
  function getOauthInfoFromCookie(req) {
61
61
  const oauthInfoStr = req.cookie(COOKIE_NAME);
@@ -29,33 +29,36 @@ __export(exports, {
29
29
  });
30
30
  var import_node_fetch = __toModule(require("node-fetch"));
31
31
  var import_utils = __toModule(require("./utils.cjs"));
32
- var stateMap = {};
32
+ var stateMap = new Map();
33
33
  function platformWebServerAuthMiddleware(server, {proxyEndpoint = import_utils.PROXY_ENDPOINT} = {}) {
34
- let redirectUri;
35
- const {myDomain, clientKey, clientSecret} = (0, import_utils.getPlatformWebServerEnvVars)();
34
+ const {clientKey, clientSecret, myDomain} = (0, import_utils.getPlatformWebServerEnvVars)();
35
+ const loginServerFallback = myDomain || import_utils.DEFAULT_LOGIN_SERVER;
36
36
  server.get(import_utils.LOGIN_ENDPOINT, (req, res) => {
37
- redirectUri = redirectUri || (0, import_utils.buildRedirectUrl)(req);
37
+ const {redirect_uri, login_server, app_path} = req.query;
38
+ const redirectUri = redirect_uri ? decodeURIComponent(redirect_uri) : (0, import_utils.buildRedirectUrl)(req);
39
+ const loginServer = login_server ? decodeURIComponent(login_server) : loginServerFallback;
40
+ const appPath = (0, import_utils.sanitizeAppPath)(app_path);
38
41
  const stateStr = (0, import_utils.generateStateString)();
39
- const appPath = (0, import_utils.getAppPath)(req);
40
- stateMap[stateStr] = appPath;
41
- const loginUri = `${myDomain}/services/oauth2/authorize?response_type=code`;
42
+ stateMap.set(stateStr, {redirectUri, loginServer, appPath});
43
+ const loginUri = `${loginServer}/services/oauth2/authorize?response_type=code`;
42
44
  res.set({
43
- Location: `${loginUri}&client_id=${clientKey}&redirect_uri=${redirectUri}&state=${stateStr}`
45
+ Location: `${loginUri}&client_id=${clientKey}&redirect_uri=${encodeURIComponent(redirectUri)}&state=${stateStr}`
44
46
  });
45
47
  res.sendStatus(302);
46
48
  });
47
49
  server.get(import_utils.REDIRECT_ENDPOINT, async (req, res) => {
48
50
  const {code, state} = req.query;
49
- const appPath = stateMap[state];
50
- if (!appPath) {
51
+ const loginState = stateMap.get(state);
52
+ if (!loginState) {
51
53
  res.status(400).send(`${import_utils.MESSAGE_PREFIX}invalid login attempt.`);
52
54
  return;
53
55
  }
54
- delete stateMap[state];
55
- const response = await (0, import_node_fetch.default)(`${myDomain}/services/oauth2/token`, {
56
+ stateMap.delete(state);
57
+ const {appPath, loginServer, redirectUri} = loginState;
58
+ const response = await (0, import_node_fetch.default)(`${loginServer}/services/oauth2/token`, {
56
59
  method: "POST",
57
60
  headers: {"Content-Type": "application/x-www-form-urlencoded", Accept: "application/json"},
58
- body: `grant_type=authorization_code&code=${code}&client_id=${clientKey}&client_secret=${clientSecret}&redirect_uri=${redirectUri}`
61
+ body: `grant_type=authorization_code&code=${code}&client_id=${clientKey}&client_secret=${clientSecret}&redirect_uri=${encodeURIComponent(redirectUri)}`
59
62
  });
60
63
  const contentType = response.headers.get("Content-Type") || "";
61
64
  const data = await (contentType.includes("application/json") ? response.json() : response.text());
@@ -68,7 +71,7 @@ function platformWebServerAuthMiddleware(server, {proxyEndpoint = import_utils.P
68
71
  res.set({Location: appPath});
69
72
  res.sendStatus(302);
70
73
  } else {
71
- const errorMsg = `${import_utils.MESSAGE_PREFIX}could not authenticate.`;
74
+ const errorMsg = `${import_utils.MESSAGE_PREFIX}could not authenticate. Error body: ${JSON.stringify(data)}`;
72
75
  console.error(errorMsg, data);
73
76
  res.status(response.status).send(errorMsg);
74
77
  }
@@ -85,4 +88,20 @@ function platformWebServerAuthMiddleware(server, {proxyEndpoint = import_utils.P
85
88
  res.status(401).send(`${import_utils.MESSAGE_PREFIX}user is not authenticated.`);
86
89
  }
87
90
  });
91
+ server.get(import_utils.REVOKE_ENDPOINT, async (req, res) => {
92
+ let returnStatus = 200;
93
+ let returnBody = null;
94
+ const oauthInfo = (0, import_utils.getOauthInfoFromCookie)(req);
95
+ if (oauthInfo) {
96
+ const {access_token, instance_url} = oauthInfo;
97
+ const url = `${instance_url}/services/oauth2/revoke?token=${access_token}`;
98
+ const proxyRes = await (0, import_node_fetch.default)(url);
99
+ const contentType = proxyRes.headers.get("Content-Type") || "";
100
+ const data = await (contentType.includes("application/json") ? proxyRes.json() : proxyRes.text());
101
+ returnStatus = proxyRes.status;
102
+ returnBody = data;
103
+ }
104
+ res.cookie(import_utils.COOKIE_NAME, "", {httpOnly: true, secure: true});
105
+ res.status(returnStatus).send(returnBody);
106
+ });
88
107
  }
@@ -1,11 +1,16 @@
1
1
  import type { RequestInit } from 'node-fetch';
2
2
  export interface PlatformWebServerInput {
3
- /** base URL to the Connected App, e.g. "https://lwr.lightning.force.com" (no trailing slash) */
4
- myDomain: string;
5
3
  /** "Consumer Key" from the Connected App */
6
4
  clientKey: string;
7
5
  /** "Consumer Secret" from the Connected App */
8
6
  clientSecret: string;
7
+ /**
8
+ * (optional) base URL to the Connected App, e.g. "https://lwr.lightning.force.com" (no trailing slash)
9
+ *
10
+ * This is not used if a request sets the "login_server" query parameter. Login will
11
+ * default to "https://login.salesforce.com" if this is not set.
12
+ */
13
+ myDomain?: string;
9
14
  }
10
15
  export interface PlatformWebServerOptions {
11
16
  /** The endpoint path through which all authenticated/proxied requests pass */
@@ -21,4 +26,9 @@ export interface RequestOptions {
21
26
  url: string;
22
27
  options: RequestInit;
23
28
  }
29
+ export interface LoginState {
30
+ appPath: string;
31
+ redirectUri: string;
32
+ loginServer: string;
33
+ }
24
34
  //# sourceMappingURL=types.d.ts.map
@@ -2,13 +2,15 @@ import type { MiddlewareRequest, Headers } from '@lwrjs/types';
2
2
  import type { OAuthInfo, RequestOptions, PlatformWebServerInput } from './types.js';
3
3
  export declare const MESSAGE_PREFIX = "Web Server OAuth Middleware - ";
4
4
  export declare const LOGIN_ENDPOINT = "/login";
5
+ export declare const REVOKE_ENDPOINT = "/revoke";
5
6
  export declare const REDIRECT_ENDPOINT = "/oauth";
6
7
  export declare const PROXY_ENDPOINT = "/services/data";
7
8
  export declare const COOKIE_NAME = "lwr_web_server_oauth_info";
9
+ export declare const DEFAULT_LOGIN_SERVER = "https://login.salesforce.com";
8
10
  export declare function buildRedirectUrl(req: MiddlewareRequest): string;
9
11
  export declare function getPlatformWebServerEnvVars(): PlatformWebServerInput;
10
12
  export declare function generateStateString(): string;
11
- export declare function getAppPath(req: MiddlewareRequest): string;
13
+ export declare function sanitizeAppPath(app_path: string | undefined): string;
12
14
  export declare function getOauthInfoFromCookie(req: MiddlewareRequest): OAuthInfo | undefined;
13
15
  export declare function buildProxyRequest(req: MiddlewareRequest, { access_token, instance_url }: OAuthInfo, existingHeader: Headers): RequestOptions;
14
16
  //# sourceMappingURL=utils.d.ts.map
package/build/es/utils.js CHANGED
@@ -1,13 +1,13 @@
1
1
  export const MESSAGE_PREFIX = 'Web Server OAuth Middleware - ';
2
2
  export const LOGIN_ENDPOINT = '/login';
3
+ export const REVOKE_ENDPOINT = '/revoke';
3
4
  export const REDIRECT_ENDPOINT = '/oauth';
4
5
  export const PROXY_ENDPOINT = '/services/data';
5
6
  export const COOKIE_NAME = 'lwr_web_server_oauth_info';
6
- const REDIRECT_QUERY = 'app_path';
7
+ export const DEFAULT_LOGIN_SERVER = 'https://login.salesforce.com';
7
8
  // Create an OAuth redirect URI from the origin of a request
8
- // Return the redirect URI encoded so it can be used as a query param
9
9
  export function buildRedirectUrl(req) {
10
- return encodeURIComponent(`${req.protocol}://${req.get('host')}${REDIRECT_ENDPOINT}`);
10
+ return `${req.protocol}://${req.get('host')}${REDIRECT_ENDPOINT}`;
11
11
  }
12
12
  // Retrieve and validate environment variables for the Web Server OAuth flow
13
13
  export function getPlatformWebServerEnvVars() {
@@ -15,11 +15,8 @@ export function getPlatformWebServerEnvVars() {
15
15
  const myDomain = process.env[env.myDomain];
16
16
  const clientKey = process.env[env.clientKey];
17
17
  const clientSecret = process.env[env.clientSecret];
18
- // The inputs are all required
18
+ // Check required vars
19
19
  const errorMsg = 'Web Server OAuth Middleware: missing process.env.';
20
- if (!myDomain) {
21
- throw new Error(`${errorMsg}${env.myDomain}`);
22
- }
23
20
  if (!clientKey) {
24
21
  throw new Error(`${errorMsg}${env.clientKey}`);
25
22
  }
@@ -34,14 +31,14 @@ export function getPlatformWebServerEnvVars() {
34
31
  export function generateStateString() {
35
32
  return `s${Math.floor(Math.random() * 0x10000).toString(16)}`;
36
33
  }
37
- // Parse the app_path query param, validate it, and return it decoded. Default = "/"
38
- export function getAppPath(req) {
39
- let appPath = req.query[REDIRECT_QUERY];
40
- if (appPath && !decodeURIComponent(appPath).startsWith('/')) {
34
+ // Ensures the given appPath is defined and decoded. Default = "/"
35
+ export function sanitizeAppPath(app_path) {
36
+ let appPath = app_path ? decodeURIComponent(app_path) : `/`;
37
+ if (!appPath.startsWith('/')) {
41
38
  console.warn('The "app_path" parameter must be a relative path staring with "/". Defaulting to "app_path" = "/".');
42
- appPath = undefined;
39
+ appPath = '/';
43
40
  }
44
- return appPath ? decodeURIComponent(appPath) : '/';
41
+ return appPath;
45
42
  }
46
43
  // Get the token and url from the cookie
47
44
  export function getOauthInfoFromCookie(req) {
@@ -1,16 +1,18 @@
1
1
  import type { PublicAppServer, ServerTypes } from '@lwrjs/types';
2
- import type { PlatformWebServerOptions } from './types.js';
3
- export declare const stateMap: {
4
- [key: string]: string;
5
- };
2
+ import type { PlatformWebServerOptions, LoginState } from './types.js';
3
+ export declare const stateMap: Map<string, LoginState>;
6
4
  /**
7
5
  * Web Server OAuth middleware: https://help.salesforce.com/s/articleView?id=sf.remoteaccess_oauth_web_server_flow.htm&type=5
8
6
  * Accepts the following environment variables:
9
- * - process.env.MY_DOMAIN
10
7
  * - process.env.CLIENT_KEY
11
8
  * - process.env.CLIENT_SECRET
9
+ * - process.env.MY_DOMAIN: (optional) The domain of the salesforce org, will be used to determine the login server if
10
+ * login_server query parameter is not set. Login server defaults to "https://login.salesforce.com"
11
+ * if both login_server nd MY_DOMAIN are not set.
12
12
  * The login endpoint accepts the following query parameters:
13
13
  * - app_path: a path encoded string; the middleware redirects to this path after setting the OAuth token in a cookie; default is "/"
14
+ * - login_server: an encoded string; the middleware attempts to login at this address; default is "https://login.salesforce.com"
15
+ * - redirect_uri: an encoded string; this is where the middleware redirects to after OAuth is done; default is calculated from the request source
14
16
  */
15
17
  export declare function platformWebServerAuthMiddleware<T extends ServerTypes>(server: PublicAppServer<T>, { proxyEndpoint }?: PlatformWebServerOptions): void;
16
18
  //# sourceMappingURL=web-server.d.ts.map
@@ -1,30 +1,37 @@
1
1
  import fetch from 'node-fetch';
2
- import { COOKIE_NAME, LOGIN_ENDPOINT, MESSAGE_PREFIX, PROXY_ENDPOINT, REDIRECT_ENDPOINT, buildProxyRequest, buildRedirectUrl, generateStateString, getAppPath, getOauthInfoFromCookie, getPlatformWebServerEnvVars, } from './utils.js';
3
- export const stateMap = {};
2
+ import { COOKIE_NAME, DEFAULT_LOGIN_SERVER, LOGIN_ENDPOINT, MESSAGE_PREFIX, PROXY_ENDPOINT, REDIRECT_ENDPOINT, REVOKE_ENDPOINT, buildProxyRequest, buildRedirectUrl, generateStateString, sanitizeAppPath, getOauthInfoFromCookie, getPlatformWebServerEnvVars, } from './utils.js';
3
+ export const stateMap = new Map();
4
4
  /**
5
5
  * Web Server OAuth middleware: https://help.salesforce.com/s/articleView?id=sf.remoteaccess_oauth_web_server_flow.htm&type=5
6
6
  * Accepts the following environment variables:
7
- * - process.env.MY_DOMAIN
8
7
  * - process.env.CLIENT_KEY
9
8
  * - process.env.CLIENT_SECRET
9
+ * - process.env.MY_DOMAIN: (optional) The domain of the salesforce org, will be used to determine the login server if
10
+ * login_server query parameter is not set. Login server defaults to "https://login.salesforce.com"
11
+ * if both login_server nd MY_DOMAIN are not set.
10
12
  * The login endpoint accepts the following query parameters:
11
13
  * - app_path: a path encoded string; the middleware redirects to this path after setting the OAuth token in a cookie; default is "/"
14
+ * - login_server: an encoded string; the middleware attempts to login at this address; default is "https://login.salesforce.com"
15
+ * - redirect_uri: an encoded string; this is where the middleware redirects to after OAuth is done; default is calculated from the request source
12
16
  */
13
17
  export function platformWebServerAuthMiddleware(server, { proxyEndpoint = PROXY_ENDPOINT } = {}) {
14
- let redirectUri;
15
- const { myDomain, clientKey, clientSecret } = getPlatformWebServerEnvVars();
18
+ const { clientKey, clientSecret, myDomain } = getPlatformWebServerEnvVars();
19
+ const loginServerFallback = myDomain || DEFAULT_LOGIN_SERVER;
16
20
  // First, request an authorization code by redirecting to Salesforce login
17
21
  // The LWR app should call this endpoint when authentication is needed
18
22
  server.get(LOGIN_ENDPOINT, (req, res) => {
19
- // Build the redirect URI and save the app path in the state map
20
- redirectUri = redirectUri || buildRedirectUrl(req);
23
+ // Parse the encoded redirect_uri, login_server, app_path query params
24
+ const { redirect_uri, login_server, app_path } = req.query;
25
+ const redirectUri = redirect_uri ? decodeURIComponent(redirect_uri) : buildRedirectUrl(req);
26
+ const loginServer = login_server ? decodeURIComponent(login_server) : loginServerFallback;
27
+ const appPath = sanitizeAppPath(app_path);
28
+ // Save the state string for verification in step 2
21
29
  const stateStr = generateStateString();
22
- const appPath = getAppPath(req);
23
- stateMap[stateStr] = appPath; // Save the state string for verification in step 2
30
+ stateMap.set(stateStr, { redirectUri, loginServer, appPath });
24
31
  // Redirect to the Salesforce login page
25
- const loginUri = `${myDomain}/services/oauth2/authorize?response_type=code`;
32
+ const loginUri = `${loginServer}/services/oauth2/authorize?response_type=code`;
26
33
  res.set({
27
- Location: `${loginUri}&client_id=${clientKey}&redirect_uri=${redirectUri}&state=${stateStr}`,
34
+ Location: `${loginUri}&client_id=${clientKey}&redirect_uri=${encodeURIComponent(redirectUri)}&state=${stateStr}`,
28
35
  });
29
36
  res.sendStatus(302);
30
37
  });
@@ -33,18 +40,19 @@ export function platformWebServerAuthMiddleware(server, { proxyEndpoint = PROXY_
33
40
  server.get(REDIRECT_ENDPOINT, async (req, res) => {
34
41
  // Parse the code and state string from the query params
35
42
  const { code, state } = req.query;
36
- const appPath = stateMap[state];
43
+ const loginState = stateMap.get(state);
37
44
  // If the state string is not found in the map, the request is invalid
38
- if (!appPath) {
45
+ if (!loginState) {
39
46
  res.status(400).send(`${MESSAGE_PREFIX}invalid login attempt.`);
40
47
  return;
41
48
  }
42
- delete stateMap[state];
49
+ stateMap.delete(state);
50
+ const { appPath, loginServer, redirectUri } = loginState;
43
51
  // Fetch the OAuth token
44
- const response = await fetch(`${myDomain}/services/oauth2/token`, {
52
+ const response = await fetch(`${loginServer}/services/oauth2/token`, {
45
53
  method: 'POST',
46
54
  headers: { 'Content-Type': 'application/x-www-form-urlencoded', Accept: 'application/json' },
47
- body: `grant_type=authorization_code&code=${code}&client_id=${clientKey}&client_secret=${clientSecret}&redirect_uri=${redirectUri}`,
55
+ body: `grant_type=authorization_code&code=${code}&client_id=${clientKey}&client_secret=${clientSecret}&redirect_uri=${encodeURIComponent(redirectUri)}`,
48
56
  });
49
57
  // Response handling
50
58
  const contentType = response.headers.get('Content-Type') || ''; // parse as json or text
@@ -63,7 +71,7 @@ export function platformWebServerAuthMiddleware(server, { proxyEndpoint = PROXY_
63
71
  }
64
72
  else {
65
73
  // Print and forward the error JSON to the client
66
- const errorMsg = `${MESSAGE_PREFIX}could not authenticate.`;
74
+ const errorMsg = `${MESSAGE_PREFIX}could not authenticate. Error body: ${JSON.stringify(data)}`;
67
75
  console.error(errorMsg, data);
68
76
  res.status(response.status).send(errorMsg);
69
77
  }
@@ -83,5 +91,23 @@ export function platformWebServerAuthMiddleware(server, { proxyEndpoint = PROXY_
83
91
  res.status(401).send(`${MESSAGE_PREFIX}user is not authenticated.`);
84
92
  }
85
93
  });
94
+ // Revoke token if user explicitly logs out
95
+ server.get(REVOKE_ENDPOINT, async (req, res) => {
96
+ let returnStatus = 200;
97
+ let returnBody = null;
98
+ const oauthInfo = getOauthInfoFromCookie(req);
99
+ if (oauthInfo) {
100
+ const { access_token, instance_url } = oauthInfo;
101
+ const url = `${instance_url}/services/oauth2/revoke?token=${access_token}`;
102
+ const proxyRes = await fetch(url);
103
+ const contentType = proxyRes.headers.get('Content-Type') || ''; // parse as json or text
104
+ const data = await (contentType.includes('application/json') ? proxyRes.json() : proxyRes.text());
105
+ returnStatus = proxyRes.status;
106
+ returnBody = data;
107
+ }
108
+ // ensure cookie is cleared
109
+ res.cookie(COOKIE_NAME, '', { httpOnly: true, secure: true });
110
+ res.status(returnStatus).send(returnBody);
111
+ });
86
112
  }
87
113
  //# sourceMappingURL=web-server.js.map
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "publishConfig": {
5
5
  "access": "public"
6
6
  },
7
- "version": "0.10.0-alpha.2",
7
+ "version": "0.10.0-alpha.21",
8
8
  "homepage": "https://developer.salesforce.com/docs/platform/lwr/overview",
9
9
  "repository": {
10
10
  "type": "git",
@@ -33,12 +33,12 @@
33
33
  "node-fetch": "^2.6.8"
34
34
  },
35
35
  "devDependencies": {
36
- "@lwrjs/server": "0.10.0-alpha.2",
37
- "@lwrjs/types": "0.10.0-alpha.2",
36
+ "@lwrjs/server": "0.10.0-alpha.21",
37
+ "@lwrjs/types": "0.10.0-alpha.21",
38
38
  "@types/node-fetch": "^2.5.12"
39
39
  },
40
40
  "engines": {
41
- "node": ">=16.0.0 <20"
41
+ "node": ">=16.0.0"
42
42
  },
43
- "gitHead": "592cda0637513885477829beea35a86407b342d4"
43
+ "gitHead": "9213e6934ff9c90f32527d761754b6db75332593"
44
44
  }