Skip to main content

Authorization: API Routes

Setting up API Routes

Before we do anything else we need to set up several API routes to handle various interactions with the WithWine API. We want to leverage Next.js server-side functionality to transmit and recieve data securely between your webserver and WithWine's API.

For the Authorization flow to operate successfully we'll need six API routes defined in ./pages/api/withwine/:

  1. /api/withwine/login
  2. /api/withwine/login-callback
  3. /api/withwine/logout
  4. /api/withwine/logout-callback
  5. /api/withwine/token-refresh
  6. /api/withwine/user

Each of these routes will carry out an important role in the WithWine Authorization Flow and are required for setps within the flow, or to get a users current Authorization state. Right now we have two options for how we'd like to handle interactions with the Login, Register and Logout links or buttons.

  1. Use a fetch to send the required requests to the API's directly with no redirects on intermediate pages
  2. Create a redirect or rewrite for each action, so that from the users perspective they are just clicking links to pages residing on your website while under the hood we're redirecting them to the appropriate API's

The first (and preferred) option is the simplest to implement and has no dependancy on redirecting at the page level, while the second option gives you some additional flexibility in the case where you may already have login and logout pages and you are looking to maintain existing website navigation patterns.

In either case you will need the five API routes outlined above, so let's take a look at these routes now in detail.

Important

At this point it's important to note that the two callbacks /login-callback and /logout-callback will need to match the values defined in your website settings on WithWine. These are defined under Custom Credentials and Configuration in the website settings in the WithWine Admin interface.

If these values do not macth those supplied by the API you'll recieve an error as outlined in the Troubleshooting guide.

If you can't match these routes directly it's recommended to use a redirect as outlined here.

/api/withwine/login

The WithWine login API route redirects the user to the WithWine authorization page, the destination is generated by the getAuthorizationPath function. The user will then complete the login process and be returned to your website.

./pages/api/withwine/login.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { getAuthorizationPath } from '@withwine/sdk';

/**
* The WithWine login route redirects the user to the WithWine authorization
* page, the destination is generated by the `getAuthorizationPath` function.
* Once logged in the user is returned to the `/LoginCallback` route.
*/
export default async function Login(req: NextApiRequest, res: NextApiResponse) {
const referer = req?.headers.referer;
const loginInfo = req?.query.loginInfo as string;
const destination = getAuthorizationPath({
siteReturnUrl: referer,
loginInfo,
});
res.redirect(307, destination);
}

You will notice we're checking the req.query.loginInfo in the code above, this is mapped to the :params* we passed in the /user/login/:params* redirect (if you are using redirects), or in the /api/withwine/login API route. This parameter defaults to login but can be supplied to the getAuthorizationPath function as register to direct the user to the WithWine Account Registration step if desired.

Example
# API Route
/api/withwine/login?loginInfo=register

# With redirects
/user/login?loginInfo=register

/api/withwine/login-callback

The Login Callback API route handles the POST response from the WithWine Authorization page and then redirects the user back to the original referring page. The getTokenUsingAuthorizationCode function handles the response and sets the required cookies.

./pages/api/withwine/login-callback.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { getTokenUsingAuthorizationCode } from '@withwine/sdk';
import { withWithWineSessionApiRoute } from '@withwine/sdk/next';

/**
* This LoginCallback API route handles the POST response from the WithWine
* Authorization login/register page and then redirects the user back to the
* original referring page.
* The getTokenUsingAuthorizationCode function handles the response and sets the
* required cookies.
*/
async function loginCallback(req: NextApiRequest, res: NextApiResponse) {
await req.body;
if (req?.body?.code && req?.body?.state) {
const { code, state } = req.body;
const result = await getTokenUsingAuthorizationCode({
code,
state,
req,
});
if (result) {
res.redirect(307, result.siteRedirectUrl);
}
} else {
req.session.destroy();
res.redirect(307, '/');
}
}

export default withWithWineSessionApiRoute(loginCallback);

/api/withwine/logout

The WithWine logout API route redirects the user to the WithWine authorization page, the destination is generated by the getLogoutPath function. Once logged out the user is returned to the /LogoutCallback route.

./pages/api/withwine/logout.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { getLogoutPath } from '@withwine/sdk';

/**
* The WithWine logout route redirects the user to the WithWine authorization
* page, the destination is generated by the `getLogoutPath` function.
* Once logged out the user is returned to the `/LogoutCallback` route.
*/
export default async function Logout(
req: NextApiRequest,
res: NextApiResponse,
) {
const referer = req?.headers.referer;
const destination = getLogoutPath({
req,
siteReturnUrl: referer,
});
res.redirect(307, destination);
}

/api/withwine/logout-callback

The Logout Callback API Route handles the GET response from the WithWine Authorization API then redirect the user to the original referring page. The logout function handles the destruction of the the appropriate cookies.

./pages/api/withwine/logout-callback.ts
import { NextApiRequest, NextApiResponse } from 'next';
import { logout } from '@withwine/sdk';
import { withWithWineSessionApiRoute } from '@withwine/sdk/next';

/**
* The Logout Callback API Route handles the GET response from the WithWine
* Authorization API then redirect the user to the original referring page.
* The logout function handles the destruction of the the appropriate cookies.
*/
async function logoutCallback(req: NextApiRequest, res: NextApiResponse) {
if (req?.query?.state) {
const state = req.query.state as string;
const result = await logout({
state,
session: req.session,
});
if (result) {
res.redirect(307, result.siteRedirectUrl);
}
} else {
req.session.destroy();
res.redirect(307, '/');
}
}

export default withWithWineSessionApiRoute(
logoutCallback
);

/api/withwine/token-refresh

The token-refresh API route refreshes the access token for the user if it has expired. This route is called internally by the useUser hook, you should never need to call it directly.

./pages/api/withwine/token-refresh.ts
import { NextApiRequest, NextApiResponse } from 'next';
import type { RefreshTokenPayload } from '@withwine/sdk/types';
import { refreshToken } from '@withwine/sdk';
import { withWithWineSessionApiRoute } from '@withwine/sdk/next';

/**
* The token-refresh API route refreshes the access token for the user.
*/
async function tokenRefresh(
req: NextApiRequest,
res: NextApiResponse<RefreshTokenPayload>,
) {
const payload = await refreshToken(req);
if (payload?.success) {
res.status(200).json(payload);
} else {
res.status(200).json({ success: false });
}
}

export default withWithWineSessionApiRoute(tokenRefresh);

/api/withwine/user

The user API route checks for the user session and returns it if found, otherwise it returns the default user object. This API is called by the useUser hook to allow your user data to be used in the UI.

./pages/api/withwine/user.ts
import { NextApiRequest, NextApiResponse } from 'next';
import type { User } from '@withwine/sdk/types';
import { defaultUser } from '@withwine/sdk';
import { withWithWineSessionApiRoute } from '@withwine/sdk/next';

/**
* The user API route checks for the user session and returns it if found,
* otherwise it returns the default user object.
*/
async function user(req: NextApiRequest, res: NextApiResponse<User>) {
if (req.session.user) {
res.json(req.session.user);
} else {
res.json(defaultUser);
}
}

export default withWithWineSessionApiRoute(user);

Redirect Configuration

If you decide to use top level redirects instead of the direct API calls via fetch we first need to add some Next.js specific configuration. in the withwine.config.json file we added earlier, we need to add a new block that will be used to instruct Next.js where to redirect various requests during the Authorization Flow. Lets add the nextjs.redirects config object to our existing configuration.

info

At this point you should have all of the necessary API routes in place so you will notice that the redirect configuration we're about to add corresponds with the API routes above.

./withwine.config.json
{
...
"nextjs": {
"redirects": [
{
"source": "/user/logout",
"destination": "/api/withwine/logout",
"permanent": false
},
{
"source": "/user/login",
"destination": "/api/withwine/login",
"permanent": false
},
{
"source": "/user/login/:params*",
"has": [
{
"type": "query",
"key": "loginInfo"
}
],
"destination": "/api/withwine/login:params*",
"permanent": false
},
],
"rewrites": [
{
"source": "/LoginCallback",
"destination": "/api/withwine/login-callback",
"permanent": false
},
{
"source": "/LogoutCallback",
"destination": "/api/withwine/logout-callback",
"permanent": false
}
]
}
}

Callback Rewrites

In the above configuration you'll notice that we have set a /LoginCallback and a /LogoutCallback redirect for the matching API routes, these are important and are added in this case becasue these values must match the values that have been set it your website settings in the WithWine backend.

We recommend that you don't change these values unless absolutely necessary, so even if you are not using redirect based routing for the /login, /logout API routes, it's highly recommended to add the redirects for the callback routes /login-callback and /logout-callback.

./withwine.config.json
{
...
"nextjs": {
"rewrites": [
{
"source": "/LoginCallback",
"destination": "/api/withwine/login-callback",
"permanent": false
},
{
"source": "/LogoutCallback",
"destination": "/api/withwine/logout-callback",
"permanent": false
}
]
}
}

Add Redirects to next.config.js

With our new additions to withwine.config.json in the root directory we now need to update next.config.js so that our redirects above can be implemented.

./next.config.js
const withwineConfig = require('./withwine.config.json');

module.exports = {
...
redirects: async () => {
return withwineConfig.nextjs.redirects.length > 0
? withwineConfig.nextjs.redirects
: [];
},
...
};
note

In the past we would probably have simply added our WithWine specific configuration to the Next.js serverRuntimeConfig or publicRuntimeConfig but as of this writing these methods have been deprecated, so we won't be using them in this instance.