I'm trying to authenticate to Xero's API. I get a 'code' which is then exchanged for an access_token. I'm still new to NextJS and React so I'm likely not thinking about this correctly.
The code I have results in the right data being returned, however I don't know how to use it effectively in the rest of the app. I couldn't figure out how to use NextAuth in a custom provider so tried to roll my own.
- The user clicks the button 'Connect to Xero' - this is a href to initiate the process and takes the user to Xero to login in the browser. User authenticates. Xero calls the callback
- the callback at /api/callback responds
- I extract the 'code' and then make the subsequent request to Xero to swap it for an access token.
This is where I get stuck - because the initial action is a href redirect, I'm not sure how to get the end API result back into my code as state/something usable. In effect Xero is calling the api/callback page and that's where the user is left.
I've tried to put useState hooks into the api/callback however that breaks the rule of hooks.
Any pointers greatly appreciated.
Code;
pages/index.js
import React from 'react'
import Layout from '../components/Layout'
import TopNav from '../components/TopNav'
import Link from 'next/link';
export default function Main(props) {
const test = props.URL
return (
<>
<Layout>
<TopNav name="Main page"/>
<p>this is the main page</p>
<Link href={test} passHref={true}>
<button className=' w-40 border rounded-md py-3 px-3 flex items-center justify-center text-sm font-medium sm:flex-1'>
Connect to Xero
</button>
</Link>
</Layout>
</>
)
}
export async function getStaticProps() {
const XeroAuthURL = "https://login.xero.com/identity/connect/authorize?response_type=code&client_id="
const client_ID = process.env.XERO_CLIENT_ID
const redirect_uri = process.env.XERO_REDIRECT_URI
const scope = "offline_access openid profile email accounting.settings"
const URL = `${XeroAuthURL}${client_ID}&redirect_uri=${redirect_uri}&scope=${scope}`
return {
props: {
URL: URL
},
};
}
/api/callback.js
import axios from "axios"
const qs = require('qs');
export default async function callback(req, res) {
try {
//callback from Xero will deliver the code, scope + state (if given)
//https://developer.xero.com/documentation/guides/oauth2/auth-flow/#2-users-are-redirected-back-to-you-with-a-code
console.log(`REQ = ${JSON.stringify(req.query)}`)
//exchange code for tokens - https://developer.xero.com/documentation/guides/oauth2/auth-flow/#3-exchange-the-code
var data = qs.stringify({
'code': req.query.code,
'grant_type': 'authorization_code',
'redirect_uri': 'http://localhost:3000/api/callback'
});
var config = {
method: 'post',
url: 'https://identity.xero.com/connect/token',
headers: {
'Content-Type': 'application/x-www-form-urlencoded',
'Authorization': 'Basic **put your authorisation result here**'
},
data : data
};
try {
const response = await axios(config)
//response has the data I want to put into State
console.log(JSON.stringify(response.data));
//save data off here somehow???
//tried redirecting but unsure if can pass the result
res.redirect(307, '/')
} catch (error) {
console.error(error)
res.status(error.status || 500).end(error.message)
}
} catch (error) {
console.error(error)
res.status(error.status || 500).end(error.message)
}
}