How to Server Side load data in App.js next.js - javascript

I have a layout component that is loaded once on App.jsx and stays the same throughout the session, but since the page is SSRed it loads first, then the layout displays after a second, is there a way to get the layout data along with the page without having to add it to every page?
function MyApp({ Component, pageProps: { session, ...pageProps } }) {
const abortController = new AbortController();
const signal = abortController.signal;
const [content, setContent] = useState();
const router = useRouter();
console.log("url", router);
useEffect(() => {
const fetchUIContent = async () => {
let data;
let responseStatus;
try {
data = await axios.get("/api/layout", {
httpsAgent: new https.Agent({ rejectUnauthorized: false }),
signal: signal,
});
responseStatus = data.status;
} catch (error) {
if (error.name === "AbortError") return;
console.log("Error message:", error.message);
} finally {
if (responseStatus == 200) {
setContent(await data.data);
} else {
console.log("Oops error", responseStatus, "occurred");
}
}
};
fetchUIContent();
return () => {
abortController.abort();
};
}, []);
return (
<Provider store={store}>
<SessionProvider session={session} /*option={{clientMaxAge: 10}}*/>
<ConfigProvider direction="ltr">
<HeadersComponent>
<Layout content={content}>
<Component {...pageProps} />
</Layout>
</HeadersComponent>
</ConfigProvider>
</SessionProvider>
</Provider>
);
}
I thought of using redux to get the layout data, but that would still need to make changes on each page and it is a pretty large project.

Fixed by https://github.com/vercel/next.js/discussions/39414
For Future readers, this is how to add extra props for use in MyApp
//to consume foo
function MyApp({ Component, pageProps, foo }) { //if you have {Component, pageProps: { session, ...pageProps }, foo} make sure foo is outside the pageProps destructure
return (
<MyFooConsumer foo={foo}>
<Component {...pageProps} />
</MyFooConsumer>
)
}
//to send foo
MyApp.getInitialProps = async (appContext) => {
// calls page's `getInitialProps` and fills `appProps.pageProps`
const appProps = await App.getInitialProps(appContext);
return { ...appProps, foo: 'bar' }
}

Related

How to handle window event listeners in react

In react i need to be able to open a popup window https://developer.mozilla.org/en-US/docs/Web/API/Window/open and manage the events such as "mesage" https://developer.mozilla.org/en-US/docs/Web/API/Window/postMessage and "load" and "close" events.
However none of the events i have added listeners to are firing...
import * as React from 'react';
import './style.css';
import { useState, useRef } from 'react';
export default function App() {
const { login, error } = useOAuth();
return (
<div>
<button onClick={login}>Login</button>
</div>
);
}
const useOAuth = () => {
const [error, setError] = useState();
const popupRef = useRef<Window | null | undefined>();
const login = () => {
popupRef.current = openPopup('https://google.com');
popupRef.current.addEventListener('load', handlePopupLoad);
popupRef.current.addEventListener('close', handlePopupClose);
popupRef.current.addEventListener('message', handlePopupMessage);
};
const handlePopupLoad = (data) => {
console.log('load', data);
};
const handlePopupClose = (data) => {
console.log('close', data);
};
const handlePopupMessage = (data) => {
console.log('message', data);
};
const openPopup = (url: string) => {
const params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
width=500,height=600,left=100,top=100`;
return window.open(url, 'Login', params);
};
return {
login,
error,
};
};
https://stackblitz.com/edit/react-ts-qlfw9q?file=App.tsx
aside:
Is there a way to differentiate between when a "user" closed the window using the "red x" button and when it was correctly closed using window.close().
how can i nicely cleanup the popup once its closed.
I have changed the URL to a local one (to avoid any cross-origin issues).
Check out the demo (If this fails to load, try refreshing. Something seems to be off with Stackblitz)
In parent page, I have used the onload (for loading), onunload (for close)
import * as React from 'react';
import { BrowserRouter, Route, Routes } from 'react-router-dom';
export default function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/auth" element={<AuthPage />} />
</Routes>
</BrowserRouter>
);
}
function Home() {
const login = () => {
console.clear();
const url = '/auth';
const popup = openPopup(url);
// When the popup loads
popup.onload = () => {
console.log('loaded. this was logged');
};
// when the popup unloads
popup.onunload = () => {
console.log('unloading now');
};
// when the popup posts a message
popup.addEventListener('message', ({ data }) => {
console.log('message: ', data);
});
};
const openPopup = (url: string) => {
const params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
width=500,height=600,left=100,top=100`;
return window.open(url, 'Login', params);
};
return (
<div>
<h1>Home</h1>
<button onClick={login}>Login</button>
</div>
);
}
function AuthPage() {
// I have added a button to trigger postMessage to parent.
const onClick = () => {
window.parent.postMessage('To parent');
};
return (
<div>
<h1>Auth Page</h1>
<button onClick={onClick}>Click me</button>
</div>
);
}
Few things I observed:
Since we are posting message from child to parent, I would expect window.addEventListener('message') to get triggered. But, for some reason, is popupRef.current.addEventListener('message') getting triggered.
popupRef.current.onunload gets triggered before the beginning of onload. If I had to guess, this is some sort of cleanup mechanism.
I think there is no react specific issue all you need to do to make this code work is to change your login function something like this.
const login = () => {
const childWindow = openPopup('https://same-origin.com');
childWindow.addEventListener('load', handlePopupLoad);
childWindow.addEventListener('close', handlePopupClose);
childWindow.addEventListener('message', handlePopupMessage);
};
well, you should probably wrap all your event listeners inside a useEffect to run it and cleanup after it, it should look like something like this
const popupRef = useRef<Window | null>(null)
const handlePopupLoad = (data: any) => {
console.log('load', data)
}
const handlePopupClose = (data: any) => {
console.log('close', data)
}
const handlePopupMessage = (data: any) => {
console.log('message', data)
}
const openPopup = (url: string) => {
const params = `scrollbars=no,resizable=no,status=no,location=no,toolbar=no,menubar=no,
width=500,height=600,left=100,top=100`
return window.open(url, 'Login', params)
}
useEffect(() => {
if (!popupRef.current) {
return undefined
}
popupRef.current = openPopup('https://google.com')
popupRef.current?.addEventListener('load', handlePopupLoad)
popupRef.current?.addEventListener('close', handlePopupClose)
popupRef.current?.addEventListener('message', handlePopupMessage)
return () => {
popupRef.current?.removeEventListener('load', handlePopupLoad)
popupRef.current?.removeEventListener('close', handlePopupClose)
popupRef.current?.removeEventListener('message', handlePopupMessage)
}
}, [popupRef])

When to use React Error Boundary Components?

When should we use Error Boundary components? Only for missing props and stuff like that?
For example, imagine this api fetching hook:
const useFetch = () => {
...
const [error, setError] = useState(null);
const method = async () => {
try {
await api.fetchData();
} catch(err) {
setError(err);
}
};
useEffect(() => {
method();
},[]);
return { ..., error };
}
Now, in a component, I just do:
const MyComponent = () => {
const { error } = useFetch();
if (error) return <FallbackUI />;
return <MainUI />;
}
Can I use an ErrorBoundary component to handle this situation (api call errors) instead of conditionally rendering?
EDIT
And what about if I only want to display a fallback UI when my fetching data method fails and there any data was previously retrieved?
Something like:
const { data, getMoreData, error } = useFetchPosts(); // data is stateful inside the hook
if (error && !data) return <FallbackUI />;
return <MainUI data={data} />;
I've followed the following approach in my projects that are all hooks/functional component implementations.
I'm using https://github.com/bvaughn/react-error-boundary
import { ErrorBoundary } from "react-error-boundary";
<ErrorBoundary FallbackComponent={ErrorFallback}>
<MyComponent />
</ErrorBoundary>
//reject the promise so it gets bubbled up
const useFetch = () => {
...
const [error, setError] = useState(null);
const method = async () => {
try {
await api.fetchData();
} catch(err) {
// setError(err);
return Promise.reject(err);
}
};
useEffect(() => {
method();
},[]);
return { ..., error };
}
function ErrorFallback({ error }: { error: any }) {
return (
<>
// you custom ui you'd like to return
</>
);
}
EDIT:
I typically have this at the top level so this is typically a catch all for all the unhandled exceptions. In other words, I wrap my App.tsx in the root index.tsx file in an ErrorBoundary. So, my code looks more like this
...
<ErrorBoundary FallbackComponent={ErrorFallback}>
<SWRConfig ...>
<React.StrictMode>
<ScrollToTop></ScrollToTop>
<App ... />
</React.StrictMode>
</SWRConfig>
</ErrorBoundary>

Routing with Spotify API

I am developing a spotify clone with the ability to play a preview of the songs and display user's different top tracks and artists. I have already made standalone pages for the website after authorizing with the help spotify-web-api-node package, but i am kinda facing a problem connecting the routers, after i login with spotify i reach my profile page where i have links to other pages, but when i try to go to another page i get an error on the server that it is an invalid authorization code and on the web console, the package throws an error that no access token was provided. I have tried every possible way to correct this but i am not able to do anything. Please help me out. The relevant code as well the whole GitHub repository is linked below:
The Github repository for this project is https://github.com/amoghkapoor/Spotify-Clone
App.js
const code = new URLSearchParams(window.location.search).get("code")
const App = () => {
return (
<>
{code ?
<Router>
<Link to="/tracks">
<div style={{ marginBottom: "3rem" }}>
<p>Tracks</p>
</div>
</Link>
<Link to="/">
<div style={{ marginBottom: "3rem" }}>
<p>Home</p>
</div>
</Link>
<Switch>
<Route exact path="/">
<Profile code={code} />
</Route>
<Route path="/tracks">
<TopTracks code={code} />
</Route>
</Switch>
</Router> : <Login />}
</>
)
}
TopTracks.js
const spotifyApi = new SpotifyWebApi({
client_id: "some client id"
})
const TopTracks = ({ code }) => {
const accessToken = useAuth(code)
console.log(accessToken) // undefined in console
console.log(code) // the correct code as provided by spotify
useEffect(() => {
if (accessToken) {
spotifyApi.setAccessToken(accessToken)
return
}
}, [accessToken])
'useAuth' custom Hook
export default function useAuth(code) {
const [accessToken, setAccessToken] = useState()
const [refreshToken, setRefreshToken] = useState()
const [expiresIn, setExpiresIn] = useState()
useEffect(() => {
axios
.post("http://localhost:3001/login", {
code
})
.then(res => {
setAccessToken(res.data.accessToken)
setRefreshToken(res.data.refreshToken)
setExpiresIn(res.data.expiresIn)
window.history.pushState({}, null, "/")
})
.catch((err) => {
// window.location = "/"
console.log("login error", err)
})
}, [code])
You don't appear to be persisting your access/refresh tokens anywhere. As soon as the component is unloaded, the data would be discarded. In addition, a sign in code is only usable once. If you use it more than once, any OAuth-compliant service will invalidate all tokens related to that code.
You can persist these tokens using localStorage, IndexedDB or another database mechanism.
For the purposes of an example (i.e. use something more secure & permanent than this), I'll use localStorage.
To help manage state across multiple views and components, you should make use of a React Context. This allows you to lift common logic higher in your component tree so that it can be reused.
Furthermore, instead of using setInterval to refresh the token periodically, you should only perform refresh operations on-demand - that is, refresh it when it expires.
// SpotifyAuthContext.js
import SpotifyWebApi from 'spotify-web-api-node';
const spotifyApi = new SpotifyWebApi({
clientId: 'fcecfc72172e4cd267473117a17cbd4d',
});
export const SpotifyAuthContext = React.createContext({
exchangeCode: () => throw new Error("context not loaded"),
refreshAccessToken: () => throw new Error("context not loaded"),
get hasToken: spotifyApi.getAccessToken() !== undefined,
api: spotifyApi
});
export const useSpotify = () => useContext(SpotifyAuthContext);
function setStoredJSON(id, obj) {
localStorage.setItem(id, JSON.stringify(obj));
}
function getStoredJSON(id, fallbackValue = null) {
const storedValue = localStorage.getItem(id);
return storedValue === null
? fallbackValue
: JSON.parse(storedValue);
}
export function SpotifyAuthContextProvider({children}) {
const [tokenInfo, setTokenInfo] = useState(() => getStoredJSON('myApp:spotify', null))
const hasToken = tokenInfo !== null
useEffect(() => {
if (tokenInfo === null) return; // do nothing, no tokens available
// attach tokens to `SpotifyWebApi` instance
spotifyApi.setCredentials({
accessToken: tokenInfo.accessToken,
refreshToken: tokenInfo.refreshToken,
})
// persist tokens
setStoredJSON('myApp:spotify', tokenInfo)
}, [tokenInfo])
function exchangeCode(code) {
return axios
.post("http://localhost:3001/login", {
code
})
.then(res => {
// TODO: Confirm whether response contains `accessToken` or `access_token`
const { accessToken, refreshToken, expiresIn } = res.data;
// store expiry time instead of expires in
setTokenInfo({
accessToken,
refreshToken,
expiresAt: Date.now() + (expiresIn * 1000)
});
})
}
function refreshAccessToken() {
return axios
.post("http://localhost:3001/refresh", {
refreshToken
})
.then(res => {
const refreshedTokenInfo = {
accessToken: res.data.accessToken,
// some refreshes may include a new refresh token!
refreshToken: res.data.refreshToken || tokenInfo.refreshToken,
// store expiry time instead of expires in
expiresAt: Date.now() + (res.data.expiresIn * 1000)
}
setTokenInfo(refreshedTokenInfo)
// attach tokens to `SpotifyWebApi` instance
spotifyApi.setCredentials({
accessToken: refreshedTokenInfo.accessToken,
refreshToken: refreshedTokenInfo.refreshToken,
})
return refreshedTokenInfo
})
}
async function refreshableCall(callApiFunc) {
if (Date.now() > tokenInfo.expiresAt)
await refreshAccessToken();
try {
return await callApiFunc()
} catch (err) {
if (err.name !== "WebapiAuthenticationError")
throw err; // rethrow irrelevant errors
}
// if here, has an authentication error, try refreshing now
return refreshAccessToken()
.then(callApiFunc)
}
return (
<SpotifyAuthContext.Provider value={{
api: spotifyApi,
exchangeCode,
hasToken,
refreshableCall,
refreshAccessToken
}}>
{children}
</SpotifyAuthContext.Provider>
)
}
Usage:
// TopTracks.js
import useSpotify from '...'
const TopTracks = () => {
const { api, refreshableCall } = useSpotify()
const [ tracks, setTracks ] = useState([])
const [ error, setError ] = useState(null)
useEffect(() => {
let disposed = false
refreshableCall(() => api.getMyTopTracks()) // <- calls getMyTopTracks, but retry if the token has expired
.then((res) => {
if (disposed) return
setTracks(res.body.items)
setError(null)
})
.catch((err) => {
if (disposed) return
setTracks([])
setError(err)
});
return () => disposed = true
});
if (error != null) {
return <span class="error">{error.message}</span>
}
if (tracks.length === 0) {
return <span class="warning">No tracks found.</span>
}
return (<ul>
{tracks.map((track) => {
const artists = track.artists
.map(artist => artist.name)
.join(', ')
return (
<li key={track.id}>
<a href={track.preview_url}>
{track.name} - {artists}
</a>
</li>
)
}
</ul>)
}
// Login.js
import useSpotify from '...'
const Login = () => {
const { exchangeCode } = useSpotify()
const [ error, setError ] = useState(null)
const code = new URLSearchParams(window.location.search).get("code")
useEffect(() => {
if (!code) return // no code. do nothing.
// if here, code available for login
let disposed = false
exchangeCode(code)
.then(() => {
if (disposed) return
setError(null)
window.history.pushState({}, null, "/")
})
.catch(error => {
if (disposed) return
console.error(error)
setError(error)
})
return () => disposed = true
}, [code])
if (error !== null) {
return <span class="error">{error.message}</span>
}
if (code) {
// TODO: Render progress bar/spinner/throbber for "Signing in..."
return /* ... */
}
// if here, no code & no error. Show login button
// TODO: Render login button
return /* ... */
}
// MyRouter.js (rename it however you like)
import useSpotify from '...'
import Login from '...'
const MyRouter = () => {
const { hasToken } = useSpotify()
if (!hasToken) {
// No access token available, show login screen
return <Login />
}
// Access token available, show main content
return (
<Router>
// ...
</Router>
)
}
// App.js
import SpotifyAuthContextProvider from '...'
import MyRouter from '...'
const App = () => {
return (
<SpotifyAuthContextProvider>
<MyRouter />
</SpotifyAuthContextProvider>
);
}

Losing router object on child component rerender

I have a parent component GoalList which maps to a child component:
{data.goals.map((item, index) => {
return (
<Link
href={{ pathname: "/goal", query: { id: item.id } }}
key={`goal-item-${index}`}
>
<a>
<li>
<div>{item.title}</div>
</li>
</a>
</Link>
);
})}
next/router's page:
import SingleGoal from "../components/SingleGoal";
const Single = () => {
return <SingleGoal />;
};
export default Single;
Child Component:
const SingleGoal = () => {
const [id, setId] = useState("");
const router = useRouter();
useEffect(() => {
if (router.query.id !== "") setId(router.query.id);
}, [router]);
const { loading, error, data } = useQuery(SINGLE_GOAL_QUERY, {
variables: { id: id },
});
if (loading) return <p>Loading...</p>;
if (error) return `Error! ${error.message}`;
return (
<div>
<h1>{data.goal.title}</h1>
<p>{data.goal.endDate}</p>
</div>
);
};
When I click on Link in the parent component, the item.id is properly transferred and the SINGLE_GOAL_QUERY executes correctly.
BUT, when I refresh the SingleGoal component, the router object takes a split second to populate, and I get a GraphQL warning:
[GraphQL error]: Message: Variable "$id" of required type "ID!" was not provided., Location: [object Object], Path: undefined
On a similar project I had previously given props to next/router's page component, but this no longer seems to work:
const Single = (props) => {
return <SingleGoal id={props.query.id} />;
};
How do I account for the delay in the router object? Is this a situation in which to use getInitialProps?
Thank you for any direction.
You can set the initial state inside your component with the router query id by reordering your hooks
const SingleGoal = () => {
const router = useRouter();
const [id, setId] = useState(router.query.id);
useEffect(() => {
if (router.query.id !== "") setId(router.query.id);
}, [router]);
const { loading, error, data } = useQuery(SINGLE_GOAL_QUERY, {
variables: { id: id },
});
if (loading) return <p>Loading...</p>;
if (error) return `Error! ${error.message}`;
return (
<div>
<h1>{data.goal.title}</h1>
<p>{data.goal.endDate}</p>
</div>
);
};
In this case, the secret to props being transferred through via the page was to enable getInitialProps via a custom _app.
Before:
const MyApp = ({ Component, apollo, pageProps }) => {
return (
<ApolloProvider client={apollo}>
<Page>
<Component {...pageProps} />
</Page>
</ApolloProvider>
);
};
After:
const MyApp = ({ Component, apollo, pageProps }) => {
return (
<ApolloProvider client={apollo}>
<Page>
<Component {...pageProps} />
</Page>
</ApolloProvider>
);
};
MyApp.getInitialProps = async ({ Component, ctx }) => {
let pageProps = {};
if (Component.getInitialProps) {
// calls page's `getInitialProps` and fills `appProps.pageProps`
pageProps = await Component.getInitialProps(ctx);
}
// exposes the query to the user
pageProps.query = ctx.query;
return { pageProps };
};
The only downfall now is that there is no more static page generation, and server-side-rendering is used on each request.

How to implement microsoft oauth in a react app that uses hash router

I am trying to implement microsoft oauth button in react that redirects/pop up allows the user to sign into their microsoft account then use the info to get the access token to then send to graph api to get user info to login. I'm using this package https://www.npmjs.com/package/msal and tried registering a url for the app to redirect to.
Problem is the react app is using hash router to lazy load pages, and in order to register the redirect url in Azure AAD it can't be a fragment. So I tried switching to Browser router and upon testing it in local host atleast got results from a successful redirect.
Then trying it in a production build could'nt get it to redirect successfully. Every time it redirected, refreshed or even wrote a different path in the url it could not find the page. I read about this issue from here React-router urls don't work when refreshing or writing manually. But now not sure how to go about fixing this. I'm fairly new to coding so any kind of help of suggestion would be appreciated.
import React, { Component } from 'react';
import { BrowserRouter,browserHistory,Router, Route, Switch } from 'react-router-dom';
import { ProtectedRoute } from './auth/ProtectedRoute'
const loading = () => <div className="animated fadeIn pt-3 text-center">Loading...</div>;
// Containers
const DefaultLayout = React.lazy(() => import('./containers/DefaultLayout'));
// Pages
const Login = React.lazy(() => import('./views/Pages/Login'));
const Register = React.lazy(() => import('./views/Pages/Register'));
const Page404 = React.lazy(() => import('./views/Pages/Page404'));
const Page500 = React.lazy(() => import('./views/Pages/Page500'));
//const EditInfo = React.lazy(() => import('./views/Pages/EditInfo'));
export class App extends Component {
render() {
return (
<BrowserRouter>
<React.Suspense fallback={loading()}>
<Switch>
<Route exact path="/login" name="Login Page" render={props => <Login {...props}/>} />
<Route exact path="/register" name="Register Page" render={props => <Register {...props}/>} />
<Route exact path="/404" name="Page 404" render={props => <Page404 {...props}/>} />
<Route exact path="/500" name="Page 500" render={props => <Page500 {...props}/>} />
<ProtectedRoute path="/" name="Home" component={DefaultLayout} />
<ProtectedRoute path="/dashboard" name="Home" component={DefaultLayout} />
</Switch>
</React.Suspense>
</BrowserRouter>
);
}
}
My function to handle the configs, redirect, requests. I'm looking to redirect to the login page. However in the production build using browser router redirects to a page that won't be found on the server.
import * as Msal from 'msal';
import axios from 'axios'
export function loginOauth () {
var msalConfig = {
auth: {
clientId: 'my client id',
redirectUri: 'http://mysite.io/login'
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: true
}
};
let loginRequest = {
scopes: ["user.read"],
prompt: 'select_account'
}
let accessTokenRequest = {
scopes: ["user.read","profile"]
}
var graphConfig = {
graphMeEndpoint: "https://graph.microsoft.com/v1.0/me"
};
var msalInstance = new Msal.UserAgentApplication(msalConfig);
let authRedirectCallBack = (errorDesc, token, error, tokenType) => {
if (token) {
console.log(token);
}
else {
console.log(error + ":" + errorDesc);
}
};
msalInstance.handleRedirectCallback(authRedirectCallBack);
let signIn = () => {
msalInstance.loginRedirect(loginRequest).then(async function (loginResponse) {
return msalInstance.acquireTokenSilent(accessTokenRequest);
}).then(function (accessTokenResponse) {
const token = accessTokenResponse.accessToken;
console.log(token);
}).catch(function (error) {
//handle error
});
}
let acquireTokenRedirectAndCallMSGraph = () => {
//Always start with acquireTokenSilent to obtain a token in the signed in user from cache
msalInstance.acquireTokenSilent(accessTokenRequest).then(function (tokenResponse) {
callMSGraph(graphConfig.graphMeEndpoint, tokenResponse.accessToken, graphAPICallback);
}).catch(function (error) {
console.log(error);
// Upon acquireTokenSilent failure (due to consent or interaction or login required ONLY)
// Call acquireTokenRedirect
if (requiresInteraction(error.errorCode)) {
msalInstance.acquireTokenRedirect(accessTokenRequest);
}
});
}
let callMSGraph = (theUrl, accessToken, callback) => {
console.log(accessToken);
var xmlHttp = new XMLHttpRequest();
xmlHttp.onreadystatechange = function () {
if (this.readyState === 4 && this.status === 200)
callback(JSON.parse(this.responseText));
}
xmlHttp.open("GET", theUrl, true); // true for asynchronous
xmlHttp.setRequestHeader('Authorization', 'Bearer ' + accessToken);
xmlHttp.send();
}
let graphAPICallback = (data) => {
console.log(data);
}
let requiresInteraction = (errorCode)=> {
if (!errorCode || !errorCode.length) {
return false;
}
return errorCode === "consent_required" ||
errorCode === "interaction_required" ||
errorCode === "login_required";
}
signIn();
}
On redirect and onloading of the page I am using the componentwillmount to get the data from the sessions.
export default class Login extends Component{
constructor(props) {
super(props);
this.state = {
modal: false,
};
this.toggle = this.toggle.bind(this);
}
toggle() {
this.setState(prevState => ({
modal: !prevState.modal,
}));
}
handleLogout(){
auth.logout(() => {console.log("logged out")})
this.toggle()
}
componentWillMount() {
var msalConfig = {
auth: {
clientId: 'my_clientid',
redirectUri: 'http://mysite.io/login'
},
cache: {
cacheLocation: "sessionStorage",
storeAuthStateInCookie: true
}
};
var msalInstance = new Msal.UserAgentApplication(msalConfig);
if (msalInstance.getAccount()) {
var tokenRequest = {
scopes: ["user.read"]
};
msalInstance.acquireTokenSilent(tokenRequest)
.then(response => {
callMSGraph(response.accessToken, (data)=>console.log(data))
// get access token from response
// response.accessToken
})
.catch(err => {
// could also check if err instance of InteractionRequiredAuthError if you can import the class.
if (err.name === "InteractionRequiredAuthError") {
return msalInstance.acquireTokenPopup(tokenRequest)
.then(response => {
// get access token from response
// response.accessToken
})
.catch(err => {
// handle error
});
}
});
} else {
// user is not logged in, you will need to log them in to acquire a token
}
let token = sessionStorage.getItem('msal.idtoken');
if(token !== null) {
var decoded = jwt_decode(token);
console.log(decoded);
} else {
console.log("NO token yet");
}
}
render() {
let open;
if(auth.isAuthenticated() === "true"){
open = true
}else{ open = false}
return (<div><button onClick={ () => {loginOauth()}} >Login with Microsoft</button> </div>);
}
You should only register the base URL you want the user redirected to. To allow your state data (i.e. your fragment) to traverse the service boundaries, you use the state parameter.
Any information you pass into the OAuth via the state parameter will be returned with your token. You then parse the state when the user is returned.

Categories