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 Nuxt 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 ./server/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 proxy 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 proxy as outlined here.

TypeScript vs JavaScript

Nuxt supports both TypeScript and JavaScript, so we'll be providing examples where it makes sense for both languages, however in most cases you can use TypeScript with minimal type inference becasue Nuxt is already providing the type definitions automatically.

/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.

./server/api/withwine/login.ts
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 defineEventHandler(async (event) => {
const query = getQuery(event);
const headers = getRequestHeaders(event);
const referer = headers?.referer;
const loginInfo = query?.loginInfo as string;
const destination = getAuthorizationPath({
siteReturnUrl: referer,
loginInfo,
})
return sendRedirect(event, destination, 307);
});

You will notice we're checking the query.loginInfo in the code above, this is mapped to the ?loginInfo=register we passed in the /withwine/register 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/withwine/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.

./server/api/withwine/login-callback.<ts|js>
import { getTokenUsingAuthorizationCode } from '@withwine/sdk';
import { withWineSessionMiddleware } from '@withwine/sdk/nuxt';

/**
* 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 withWineSessionMiddleware returns the event with the session attached.
* The getTokenUsingAuthorizationCode function handles the response and sets the
* required cookies.
*/
export default defineEventHandler(async (event) => {
const wwEvent = await withWineSessionMiddleware(event);
const { req } = wwEvent.node;
const body = await readBody(wwEvent);
if (body?.code && body?.state) {
const { code, state } = body;
const result = await getTokenUsingAuthorizationCode({
code,
state,
req,
});
if (result) {
return sendRedirect(wwEvent, result.siteRedirectUrl, 307);
}
} else {
req.session.destroy();
return sendRedirect(wwEvent, '/', 307);
}
});

/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.

./server/api/withwine/logout.<ts|js>
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 defineEventHandler(async (event) => {
const headers = getRequestHeaders(event);
const referer = headers?.referer;
const destination = getLogoutPath({
req: event.node.req,
siteReturnUrl: referer,
});
return sendRedirect(event, destination, 307);
});

/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.

./server/api/withwine/logout-callback.<ts|js>
import { logout } from '@withwine/sdk';
import { withWineSessionMiddleware } from '@withwine/sdk/nuxt';

/**
* 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.
*/
export default defineEventHandler(async (event) => {
const wwEvent = await withWineSessionMiddleware(event);
const { req } = wwEvent.node;
if (req?.query?.state) {
const state = req.query.state;
const result = await logout({
state,
session: req.session,
});
if (result) {
return sendRedirect(wwEvent, result.siteRedirectUrl, 307);
}
} else {
req.session.destroy();
return sendRedirect(wwEvent, '/', 307);
}
});

/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 composable, you should never need to call it directly.

./server/api/withwine/token-refresh.<ts|js>
import { refreshToken } from '@withwine/sdk';
import { withWineSessionMiddleware } from '@withwine/sdk/nuxt';

/**
* The token-refresh API route refreshes the access token for the user.
*/
export default defineEventHandler(async (event) => {
const wwEvent = await withWineSessionMiddleware(event);
const { req } = wwEvent.node;
const payload = await refreshToken(req);
if (payload?.success) {
return payload;
} else {
return { success: false };
}
});

/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 composable to allow your user data to be used in the UI.

./server/api/withwine/user.<ts|js>
import { defaultUser } from '@withwine/sdk';
import { withWineSessionMiddleware } from '@withwine/sdk/nuxt';

/**
* The user API route checks for the user session and returns it if found,
* otherwise it returns the default user object.
*/
export default defineEventHandler(async (event) => {
const wwEvent = await withWineSessionMiddleware(event);
const { req } = wwEvent.node;
if (req.session.user) {
return req.session.user;
} else {
return defaultUser;
}
});

Redirect Configuration

If you decide to use top level redirects instead of the direct API calls via fetch we first need to add some Nuxt specific configuration. in the withwine.config.json file we added earlier, we need to add a new block that will be used to instruct Nuxt where to redirect various requests during the Authorization Flow. Lets add the nuxt.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
{
...
"nuxt": {
"routeRules": {
"/withwine/logout": {
"redirect": "/api/withwine/logout"
},
"/withwine/login": {
"redirect": "/api/withwine/login"
},
"/withwine/register": {
"redirect": "/api/withwine/login?loginInfo=register"
},
"/LoginCallback": {
"proxy": "/api/withwine/login-callback"
},
"/LogoutCallback": {
"proxy": "/api/withwine/logout-callback"
}
}
}
}

Callback Proxies

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 proxy for the callback routes /login-callback and /logout-callback.

./withwine.config.json
{
...
"nuxt": {
"routeRules": {
"/LoginCallback": {
"proxy": "/api/withwine/login-callback"
},
"/LogoutCallback": {
"proxy": "/api/withwine/logout-callback"
}
}
}
}

Add Redirects to nuxt.config.js

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

./nuxt.config.js
import withWineConfig from './withwine.config.json';

export default defineNuxtConfig({
...
routeRules: {
...withWineConfig.nuxt.routeRules,
},
...
});