Repository: auth0/nextjs-auth0
Pull request #220 (2021-01-05 12:28:30)

Updates from testing/reviewing
Couple of updates required after testing and reviewing the docs:

- `initAuth0` shouldn't throw in browser mode
- add a test for `baseURL` wrangling
- Add `httpTimeout` option to match v0.x
- Add missing option to configure callback with env var
- Update examples per @jamesqquick suggestions
- remove TODO for profile url, decided to go with `/me` in the end


Patch:
diff --git a/EXAMPLES.md b/EXAMPLES.md
index 0203e3f2..249f5738 100644
--- a/EXAMPLES.md
+++ b/EXAMPLES.md
@@ -3,9 +3,9 @@
 - [Basic Setup](#basic-setup)
 - [Customise handlers behaviour](#customise-handlers-behaviour)
 - [Use custom auth urls](#use-custom-auth-urls)
-- [Protect an API Route](#protect-an-api-route)
 - [Protecting a Server Side Rendered (SSR) Page](#protecting-a-server-side-rendered-ssr-page)
 - [Protecting a Client Side Rendered (CSR) Page](#protecting-a-client-side-rendered-csr-page)
+- [Protect an API Route](#protect-an-api-route)
 - [Access an External API from an API Route](#access-an-external-api-from-an-api-route)
 - [Access an External API from the front end](#access-an-external-api-from-the-front-end)
 - [Create your own instance of the SDK](#create-your-own-instance-of-the-sdk)
@@ -134,6 +134,40 @@ export default () => <a href="/api/custom-login">Login</a>;
 
 > Note: you will need to specify this custom login URL when calling `withPageAuthRequired` both the [front end version](https://auth0.github.io/nextjs-auth0/interfaces/frontend_with_page_auth_required.withpageauthrequiredoptions.html#loginurl) and [server side version](https://auth0.github.io/nextjs-auth0/modules/helpers_with_page_auth_required.html#withpageauthrequiredoptions)
 
+## Protecting a Server Side Rendered (SSR) Page
+
+Requests to `/pages/profile` without a valid session cookie will be redirected to the login page.
+
+```javascript
+// pages/profile.js
+import { withPageAuthRequired } from '@auth0/nextjs-auth0';
+
+export default function Profile({ user }) {
+  return <div>Hello {user.name}</div>;
+}
+
+// You can optionally pass your own `getServerSideProps` function into
+// `withPageAuthRequired` and the props will be merged with the `user` prop
+export const getServerSideProps = withPageAuthRequired();
+```
+
+See a running example of a [SSR protected page](./examples/kitchen-sink-example/pages/profile-ssr.tsx) in the kitchen-sink example app.
+
+## Protecting a Client Side Rendered (CSR) Page
+
+Requests to `/pages/profile` without a valid session cookie will be redirected to the login page.
+
+```javascript
+// pages/profile.js
+import { withPageAuthRequired } from '@auth0/nextjs-auth0';
+
+export default withPageAuthRequired(function Profile({ user }) {
+  return <div>Hello {user.name}</div>;
+});
+```
+
+See a running example of a [CSR protected page](./examples/kitchen-sink-example/pages/profile.tsx) in the kitchen-sink example app.
+
 ## Protect an API Route
 
 Requests to `/pages/api/protected` without a valid session cookie will fail with `401`.
@@ -143,7 +177,7 @@ Requests to `/pages/api/protected` without a valid session cookie will fail with
 import { withApiAuthRequired } from '@auth0/nextjs-auth0';
 
 export default withApiAuthRequired(async function myApiRoute(req, res) {
-  res.json({ protected });
+  res.json({ protected: 'My Secret' });
 });
 ```
 
@@ -170,40 +204,6 @@ export default withPageAuthRequired(function Products() {
 See a running example in the kitchen-sink example app, the [protected API route](./examples/kitchen-sink-example/pages/api/shows.ts) and
 the [frontend code to access the protected API](./examples/kitchen-sink-example/pages/shows.tsx).
 
-## Protecting a Server Side Rendered (SSR) Page
-
-Requests to `/pages/profile` without a valid session cookie will be redirected to the login page.
-
-```javascript
-// pages/profile.js
-import { withPageAuthRequired } from '@auth0/nextjs-auth0';
-
-export default function Profile({ user }) {
-  return <div>Hello {user.name}</div>;
-}
-
-// You can optionally pass your own `getServerSideProps` function into
-// `withPageAuthRequired` and the props will be merged with the `user` prop
-export const getServerSideProps = withPageAuthRequired();
-```
-
-See a running example of a [SSR protected page](./examples/kitchen-sink-example/pages/profile-ssr.tsx) in the kitchen-sink example app.
-
-## Protecting a Client Side Rendered (CSR) Page
-
-Requests to `/pages/profile` without a valid session cookie will be redirected to the login page.
-
-```javascript
-// pages/profile.js
-import { withPageAuthRequired } from '@auth0/nextjs-auth0';
-
-export default withPageAuthRequired(function Profile({ user }) {
-  return <div>Hello {user.name}</div>;
-});
-```
-
-See a running example of a [CSR protected page](./examples/kitchen-sink-example/pages/profile.tsx) in the kitchen-sink example app.
-
 ## Access an External API from an API Route
 
 Get an Access Token by specifying `response_type: 'code'` and providing your API's audience and scopes.
diff --git a/src/auth0-session/client.ts b/src/auth0-session/client.ts
index d21dc1bc..753c531a 100644
--- a/src/auth0-session/client.ts
+++ b/src/auth0-session/client.ts
@@ -46,7 +46,7 @@ export default function get(config: Config, { name, version }: Telemetry): Clien
             }
           : undefined)
       },
-      timeout: 5000
+      timeout: config.httpTimeout
     });
     const applyHttpOptionsCustom = (entity: Issuer<Client> | typeof Issuer | Client): void => {
       // eslint-disable-next-line no-param-reassign
diff --git a/src/auth0-session/config.ts b/src/auth0-session/config.ts
index 54845fff..0dabaa08 100644
--- a/src/auth0-session/config.ts
+++ b/src/auth0-session/config.ts
@@ -102,6 +102,12 @@ export interface Config {
    */
   clockTolerance: number;
 
+  /**
+   * Integer value for the http timeout in ms for authentication requests.
+   * Default is 5000
+   */
+  httpTimeout: number;
+
   /**
    * To opt-out of sending the library and node version to your authorization server
    * via the `Auth0-Client` header. Default is `true
diff --git a/src/auth0-session/get-config.ts b/src/auth0-session/get-config.ts
index 0dd7789b..f8a50892 100644
--- a/src/auth0-session/get-config.ts
+++ b/src/auth0-session/get-config.ts
@@ -118,6 +118,7 @@ const paramsSchema = Joi.object({
       }
     ),
   clockTolerance: Joi.number().optional().default(60),
+  httpTimeout: Joi.number().optional().default(5000),
   enableTelemetry: Joi.boolean().optional().default(true),
   getLoginState: Joi.function()
     .optional()
diff --git a/src/config.ts b/src/config.ts
index a3ff459c..f17486e5 100644
--- a/src/config.ts
+++ b/src/config.ts
@@ -34,11 +34,13 @@ import { LoginOptions, DeepPartial } from './auth0-session';
  * ### Optional
  *
  * - `AUTH0_CLOCK_TOLERANCE`: See {@link clockTolerance}
+ * - `AUTH0_HTTP_TIMEOUT`: See {@link httpTimeout}
  * - `AUTH0_ENABLE_TELEMETRY`: See {@link enableTelemetry}
  * - `AUTH0_IDP_LOGOUT`: See {@link idpLogout}
  * - `AUTH0_ID_TOKEN_SIGNING_ALG`: See {@link idTokenSigningAlg}
  * - `AUTH0_LEGACY_SAME_SITE_COOKIE`: See {@link legacySameSiteCookie}
  * - `AUTH0_POST_LOGOUT_REDIRECT`: See {@link Config.routes}
+ * - `AUTH0_CALLBACK`: See {@link Config.routes}
  * - `AUTH0_AUDIENCE`: See {@link Config.authorizationParams}
  * - `AUTH0_SCOPE`: See {@link Config.authorizationParams}
  * - `AUTH0_SESSION_NAME`: See {@link SessionConfig.name}
@@ -150,6 +152,12 @@ export interface Config {
    */
   clockTolerance: number;
 
+  /**
+   * Integer value for the http timeout in ms for authentication requests.
+   * Default is 5000
+   */
+  httpTimeout: number;
+
   /**
    * To opt-out of sending the library and node version to your authorization server
    * via the `Auth0-Client` header. Default is `true
@@ -379,11 +387,13 @@ export const getParams = (params?: ConfigParameters): ConfigParameters => {
     AUTH0_CLIENT_ID,
     AUTH0_CLIENT_SECRET,
     AUTH0_CLOCK_TOLERANCE,
+    AUTH0_HTTP_TIMEOUT,
     AUTH0_ENABLE_TELEMETRY,
     AUTH0_IDP_LOGOUT,
     AUTH0_ID_TOKEN_SIGNING_ALG,
     AUTH0_LEGACY_SAME_SITE_COOKIE,
     AUTH0_POST_LOGOUT_REDIRECT,
+    AUTH0_CALLBACK,
     AUTH0_AUDIENCE,
     AUTH0_SCOPE,
     AUTH0_SESSION_NAME,
@@ -398,7 +408,8 @@ export const getParams = (params?: ConfigParameters): ConfigParameters => {
     AUTH0_COOKIE_SAME_SITE
   } = process.env;
 
-  const baseURL = AUTH0_BASE_URL && !AUTH0_BASE_URL.startsWith('http') ? `https://${AUTH0_BASE_URL}` : AUTH0_BASE_URL;
+  const baseURL =
+    AUTH0_BASE_URL && !/^https?:\/\//.test(AUTH0_BASE_URL as string) ? `https://${AUTH0_BASE_URL}` : AUTH0_BASE_URL;
 
   return {
     secret: AUTH0_SECRET,
@@ -407,6 +418,7 @@ export const getParams = (params?: ConfigParameters): ConfigParameters => {
     clientID: AUTH0_CLIENT_ID,
     clientSecret: AUTH0_CLIENT_SECRET,
     clockTolerance: num(AUTH0_CLOCK_TOLERANCE),
+    httpTimeout: num(AUTH0_HTTP_TIMEOUT),
     enableTelemetry: bool(AUTH0_ENABLE_TELEMETRY),
     idpLogout: bool(AUTH0_IDP_LOGOUT, true),
     auth0Logout: bool(AUTH0_IDP_LOGOUT, true),
@@ -439,7 +451,7 @@ export const getParams = (params?: ConfigParameters): ConfigParameters => {
       }
     },
     routes: {
-      callback: '/api/auth/callback',
+      callback: AUTH0_CALLBACK || '/api/auth/callback',
       postLogoutRedirect: AUTH0_POST_LOGOUT_REDIRECT,
       ...params?.routes
     }
diff --git a/src/frontend/use-user.tsx b/src/frontend/use-user.tsx
index 20e3dbff..b40a3ce8 100644
--- a/src/frontend/use-user.tsx
+++ b/src/frontend/use-user.tsx
@@ -102,7 +102,7 @@ export type UserProvider = (props: UserProviderProps) => ReactElement<UserContex
 export default ({
   children,
   user: initialUser,
-  profileUrl = '/api/auth/me' // TODO: Change to /api/auth/user
+  profileUrl = '/api/auth/me'
 }: UserProviderProps): ReactElement<UserContext> => {
   const [user, setUser] = useState<UserProfile | undefined>(() => initialUser);
   const [loading, setLoading] = useState<boolean>(() => !initialUser);
diff --git a/src/index.browser.ts b/src/index.browser.ts
index 70eceef4..04eb734b 100644
--- a/src/index.browser.ts
+++ b/src/index.browser.ts
@@ -1,4 +1,4 @@
-import { InitAuth0 } from './instance';
+import { InitAuth0, SignInWithAuth0 } from './instance';
 import { GetAccessToken, GetSession } from './session';
 import { WithApiAuthRequired } from './helpers';
 import { HandleAuth, HandleCallback, HandleLogin, HandleLogout, HandleProfile } from './handlers';
@@ -11,30 +11,42 @@ export {
   WithPageAuthRequired
 } from './frontend';
 
-export const initAuth0: InitAuth0 = () => {
-  throw new Error('The initAuth0 method can only be used from the server side');
-};
-export const getSession: GetSession = () => {
-  throw new Error('The getSession method can only be used from the server side');
-};
-export const getAccessToken: GetAccessToken = () => {
-  throw new Error('The getAccessToken method can only be used from the server side');
-};
-export const withApiAuthRequired: WithApiAuthRequired = () => {
-  throw new Error('The withApiAuthRequired method can only be used from the server side');
-};
-export const handleLogin: HandleLogin = () => {
-  throw new Error('The handleLogin method can only be used from the server side');
-};
-export const handleLogout: HandleLogout = () => {
-  throw new Error('The handleLogout method can only be used from the server side');
-};
-export const handleCallback: HandleCallback = () => {
-  throw new Error('The handleCallback method can only be used from the server side');
-};
-export const handleProfile: HandleProfile = () => {
-  throw new Error('The handleProfile method can only be used from the server side');
-};
-export const handleAuth: HandleAuth = () => {
-  throw new Error('The handleAuth method can only be used from the server side');
+const instance: SignInWithAuth0 = {
+  getSession() {
+    throw new Error('The getSession method can only be used from the server side');
+  },
+  getAccessToken() {
+    throw new Error('The getAccessToken method can only be used from the server side');
+  },
+  withApiAuthRequired() {
+    throw new Error('The withApiAuthRequired method can only be used from the server side');
+  },
+  handleLogin() {
+    throw new Error('The handleLogin method can only be used from the server side');
+  },
+  handleLogout() {
+    throw new Error('The handleLogout method can only be used from the server side');
+  },
+  handleCallback() {
+    throw new Error('The handleCallback method can only be used from the server side');
+  },
+  handleProfile() {
+    throw new Error('The handleProfile method can only be used from the server side');
+  },
+  handleAuth() {
+    throw new Error('The handleAuth method can only be used from the server side');
+  },
+  withPageAuthRequired() {
+    throw new Error('The withPageAuthRequired method can only be used from the server side');
+  }
 };
+
+export const initAuth0: InitAuth0 = () => instance;
+export const getSession: GetSession = (...args) => instance.getSession(...args);
+export const getAccessToken: GetAccessToken = (...args) => instance.getAccessToken(...args);
+export const withApiAuthRequired: WithApiAuthRequired = (...args) => instance.withApiAuthRequired(...args);
+export const handleLogin: HandleLogin = (...args) => instance.handleLogin(...args);
+export const handleLogout: HandleLogout = (...args) => instance.handleLogout(...args);
+export const handleCallback: HandleCallback = (...args) => instance.handleCallback(...args);
+export const handleProfile: HandleProfile = (...args) => instance.handleProfile(...args);
+export const handleAuth: HandleAuth = (...args) => instance.handleAuth(...args);
diff --git a/src/version.ts b/src/version.ts
index feecf4a7..3a1f0c99 100644
--- a/src/version.ts
+++ b/src/version.ts
@@ -1,1 +1,1 @@
-export default '0.16.0';
+export default '1.0.0-beta.0';


Test patch:
diff --git a/tests/config.test.ts b/tests/config.test.ts
index 0443ede3..7207c55c 100644
--- a/tests/config.test.ts
+++ b/tests/config.test.ts
@@ -36,6 +36,7 @@ describe('config params', () => {
       clientID: undefined,
       clientSecret: undefined,
       clockTolerance: undefined,
+      httpTimeout: undefined,
       enableTelemetry: undefined,
       idTokenSigningAlg: undefined,
       idpLogout: true,
@@ -98,11 +99,13 @@ describe('config params', () => {
     expect(
       getParamsWithEnv({
         AUTH0_CLOCK_TOLERANCE: '100',
+        AUTH0_HTTP_TIMEOUT: '9999',
         AUTH0_SESSION_ROLLING_DURATION: '0',
         AUTH0_SESSION_ABSOLUTE_DURATION: '1'
       })
     ).toMatchObject({
       clockTolerance: 100,
+      httpTimeout: 9999,
       session: {
         rolling: undefined,
         rollingDuration: 0,
@@ -151,4 +154,24 @@ describe('config params', () => {
       }
     });
   });
+
+  test('should allow hostnames as baseURL', () => {
+    expect(
+      getParamsWithEnv({
+        AUTH0_BASE_URL: 'foo.auth0.com'
+      })
+    ).toMatchObject({
+      baseURL: 'https://foo.auth0.com'
+    });
+  });
+
+  test('should accept optional callback path', () => {
+    expect(
+      getParamsWithEnv({
+        AUTH0_CALLBACK: '/api/custom-callback'
+      })
+    ).toMatchObject({
+      routes: expect.objectContaining({ callback: '/api/custom-callback' })
+    });
+  });
 });