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/
:
/api/withwine/login
/api/withwine/login-callback
/api/withwine/logout
/api/withwine/logout-callback
/api/withwine/token-refresh
/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.
- Use a
fetch
to send the required requests to the API's directly with no redirects on intermediate pages - 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.
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.
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.
- TypeScript
- JavaScript
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);
});
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;
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.
/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.
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.
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.
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.
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.
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.
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.
{
...
"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
.
{
...
"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.
import withWineConfig from './withwine.config.json';
export default defineNuxtConfig({
...
routeRules: {
...withWineConfig.nuxt.routeRules,
},
...
});