NextAuth: authorize all APP with Layout wrapping - email

I'm trying to use NextAuth for authorize access to my app and I have two main issue.
1 - all the app is wrapped with a Layout in _app.js
<SessionProvider session={pageProps.session}>
<RecoilRoot>
<Layout>
<Component {...pageProps} />
</Layout>
</RecoilRoot>
</SessionProvider>
I'm using the email Provider and if I use the built in signin page everything works great but if I add a custom signin page under pages/signin.js I'm not able to proceed. My undeerstanding is that with custom signin page everything is wrapped with the layout. The layout has a useeffect with a call to an api to get items to show in the menu
import Navbar from "./navbar";
import Sidebar from "./sidebar";
import Main from "./main";
import { useEffect, useState } from "react";
const Layout = ({ children }) => {
const [menu, setMenu] = useState();
useEffect(async () => {
const data = await fetch("api/menu/menu", {
method: "GET",
headers: { "Content-Type": "application/json" },
});
const menuItems = await data.json();
setMenu(menuItems.data);
}, []);
return (
<>
{menu && <Navbar menu={menu.profile} />}
{menu && <Sidebar menu={menu.sidebar} />}
<Main children={children}></Main>
</>
);
};
export default Layout;
In _app.js I'm not able to retrieve the session to conditonally render the page.
Important to note that I'm using _middleware.js to protect all the app and define the custome signin page like so
import withAuth from "next-auth/middleware";
export default withAuth({
pages: {
signIn: "/auth/customSignin",
},
});
Does anyone is able to pint me in the right direction?
2 issue- I'd like top verify if user exists in the database before sending the email. Am I right I can handle it in the signin callback? If yes can I add a second filed like poassword so I can both verify user exists in db, verify with password and then send email?
Many thanks for any help

I solved the first issue using useRouter and checking the pathname for the customSignin page.
if (router.pathname === "/auth/customSignin") {
return (
<SessionProvider session={pageProps.session}>
<Component {...pageProps} />
</SessionProvider>
);
}
return (
<SessionProvider session={pageProps.session}>
<RecoilRoot>
<Layout>
<Component {...pageProps} />
</Layout>
</RecoilRoot>
</SessionProvider>
);
}
export default MyApp;
Now I'm dealing with the second one. Anybody can help on that.
Basically I need to add a second field to the built in Email Provider and check that field against the database

Related

In SolidJS, How Do I Do a Fallback Route?

I'm new to Solid JS, coming mainly from a React background. I'm using Solid and Solid-App-Router right now for the first time.
I'm trying to design routes with a fallback, meaning if an endpoint is put into the URL that does not exist, it will redirect to a default location. My problem is this fallback is executing no matter what, overriding all my other routes.
I'll add that namedLazy works great and is simply a wrapper to support named exports with SolidJS lazy(). Here is my routing code:
import { namedLazy } from '../../utils/solidWrappers';
import { Routes, Route } from 'solid-app-router';
import { isAuthenticated } from '../../resources/AuthResources';
const Welcome = namedLazy(() => import('./Welcome'), 'Welcome');
const Categories = namedLazy(() => import('./Categories'), 'Categories');
const Redirect = namedLazy(() => import('../UI/Redirect'), 'Redirect');
export const AppRoutes = () => {
return (
<Routes>
<Route path="/welcome" element={<Welcome />} />
{isAuthenticated() && (
<Route path="/categories" element={<Categories />} />
)}
<Route path="*" element={<Redirect />} />
</Routes>
);
};
And here is my Redirect component:
import { useNavigate } from 'solid-app-router';
export const Redirect = () => {
const navigate = useNavigate();
navigate('/welcome');
return <></>;
};
This kind of fallback route design works in react-router, however it's not working for me with solid-app-router. This is not the only route design, I also tried the configuration/array based route design as well and had the same problem. I'm open to suggestions for how to properly implement this functionality.
As the other commenter said, my code for the fallback route actually does work. This was a reactivity issue.
When the page first loads, isAuthenticated() returns false, because the authentication check is an ajax call that hadn't run yet. Therefore the /categories route wouldn't be rendered, and if I was trying to manually navigate to /categories I would instead by redirected. This made it appear as though the catch-all route was overriding everything, when in fact it was behaving as expected.
I added another check to prevent the routes from rendering until after the authentication check ajax call was made, and then everything worked perfectly.

Mocking authentication when testing MSAL React Apps

Our app is wrapped in the MSAL Authentication Template from #azure/msal-react in a standard way - key code segments are summarized below.
We would like to test app's individual components using react testing library (or something similar). Of course, when a React component such as SampleComponentUnderTest is to be properly rendered by a test as is shown in the simple test below, it must be wrapped in an MSAL component as well.
Is there a proper way to mock the MSAL authentication process for such purposes? Anyway to wrap a component under test in MSAL and directly provide test user's credentials to this component under test? Any references to useful documentation, blog posts, video, etc. to point us in the right direction would be greatly appreciated.
A Simple test
test('first test', () => {
const { getByText } = render(<SampleComponentUnderTest />);
const someText = getByText('A line of text');
expect(someText).toBeInTheDocument();
});
Config
export const msalConfig: Configuration = {
auth: {
clientId: `${process.env.REACT_APP_CLIENT_ID}`,
authority: `https://login.microsoftonline.com/${process.env.REACT_APP_TENANT_ID}`,
redirectUri:
process.env.NODE_ENV === 'development'
? 'http://localhost:3000/'
: process.env.REACT_APP_DEPLOY_URL,
},
cache: {
cacheLocation: 'sessionStorage',
storeAuthStateInCookie: false,
},
system: {
loggerOptions: {
loggerCallback: (level, message, containsPii) => {
if (containsPii) {
return;
}
switch (level) {
case LogLevel.Error:
console.error(message);
return;
case LogLevel.Info:
console.info(message);
return;
case LogLevel.Verbose:
console.debug(message);
return;
case LogLevel.Warning:
console.warn(message);
return;
default:
console.error(message);
}
},
},
},
};
Main app component
const msalInstance = new PublicClientApplication(msalConfig);
<MsalProvider instance={msalInstance}>
{!isAuthenticated && <UnauthenticatedHomePage />}
{isAuthenticated && <Protected />}
</MsalProvider>
Unauthenticated component
const signInClickHandler = (instance: IPublicClientApplication) => {
instance.loginRedirect(loginRequest).catch((e) => {
console.log(e);
});
};
<UnauthenticatedTemplate>
<Button onClick={() => signInClickHandler(instance)}>Sign in</Button>
</UnauthenticatedTemplate>
Protected component
<MsalAuthenticationTemplate
interactionType={InteractionType.Redirect}
errorComponent={ErrorComponent}
loadingComponent={LoadingComponent}
>
<SampleComponentUnderTest />
</MsalAuthenticationTemplate>
I had the same issue as you regarding component's test under msal-react.
It took me a couple of days to figure out how to implement a correct auth mock.
That's why I've created a package you will find here, that encapsulates all the boilerplate code : https://github.com/Mimetis/msal-react-tester
Basically, you can do multiple scenaris (user is already logged, user is not logged, user must log in etc ...) in a couple of lines, without having to configure anything and of course without having to reach Azure AD in any cases:
describe('Home page', () => {
let msalTester: MsalReactTester;
beforeEach(() => {
// new instance of msal tester for each test
msalTester = new MsalReactTester();
// spy all required msal things
msalTester.spyMsal();
});
afterEach(() => {
msalTester.resetSpyMsal();
});
test('Home page render correctly when user is logged in', async () => {
msalTester.isLogged();
render(
<MsalProvider instance={msalTester.client}>
<MemoryRouter>
<Layout>
<HomePage />
</Layout>
</MemoryRouter>
</MsalProvider>,
);
await msalTester.waitForRedirect();
let allLoggedInButtons = await screen.findAllByRole('button', { name: `${msalTester.activeAccount.name}` });
expect(allLoggedInButtons).toHaveLength(2);
});
test('Home page render correctly when user logs in using redirect', async () => {
msalTester.isNotLogged();
render(
<MsalProvider instance={msalTester.client}>
<MemoryRouter>
<Layout>
<HomePage />
</Layout>
</MemoryRouter>
</MsalProvider>,
);
await msalTester.waitForRedirect();
let signin = screen.getByRole('button', { name: 'Sign In - Redirect' });
userEvent.click(signin);
await msalTester.waitForLogin();
let allLoggedInButtons = await screen.findAllByRole('button', { name: `${msalTester.activeAccount.name}` });
expect(allLoggedInButtons).toHaveLength(2);
});
I am also curious about this, but from a slightly different perspective. I am trying to avoid littering the code base with components directly from msal in case we want to swap out identity providers at some point. The primary way to do this is to use a hook as an abstraction layer such as exposing isAuthenticated through that hook rather than the msal component library itself.
The useAuth hook would use the MSAL package directly. For the wrapper component however, I think we have to just create a separate component that either returns the MsalProvider OR a mocked auth provider of your choice. Since MsalProvider uses useContext beneath the hood I don't think you need to wrap it in another context provider.
Hope these ideas help while you are thinking through ways to do this. Know this isn't a direct answer to your question.

Getting a dialog on click of Edit Button on admin-on-rest

I am working on an application using admin-on-rest framework. For editing an entry on a Resource we provide XXXEdit, XXXShow, XXXCreate props to it. My requirement is that when I click on an Edit button in List view on any entry I should get a Dialog box with the parameters in XXXEdit instead of going to a new page. I tried doing this by using a Dialog in XXXEdit component
<Edit title={<RoleTitle />} {...props}>
<SimpleForm>
<Dialog
title="Dialog With Actions"
actions={actions}
modal={false}
open={true}
>
<TextInput source="id" />
<TextInput source="name" validate={required} />
.
.//some more fields
</Dialog>
</SimpleForm>
</Edit>
I get errors like The TextInput component wasn't called within a redux-form
If I use a DisabledInput then I get an error cannot read value of undefined
How do I go on with this?
I do not think you can use Simpleform for this. You will need to create a custom Form using Redux-Form. Look at the bottom answer that documents the final answer.
This might help you
How to richly style AOR Edit page
Instead of creating a page. You are creating a component that connects to the Redux state and displays as a dialog box.
I tried to resolve this using HOC and react-router.
I created a button using AOR button and provided a containerElement
containerElement={
<Link
key={record.id}
to={{
...{
pathname: `${basePath}/${encodeURIComponent(record.id)}`
},
...{ state: { modal: true } }
}}
/>
}
I created a route like this where DialogRoleEdit is an AOR edit component wrapped with a dialog HOC below .
<Route
exact
path="/roles/:id"
render={routeProps => {
return !!(
routeProps.location.state && routeProps.location.state.modal
) ? (
<Restricted authClient={authClient} location={routeProps.location}>
<div>
<RoleList resource={"roles"} {...routeProps} />
<DialogRoleEdit resource={"roles"} {...routeProps} />
</div>
</Restricted>
) : (
<Restricted authClient={authClient} location={routeProps.location}>
<RoleEdit resource={"roles"} {...routeProps} />
</Restricted>
);
}}
/>
Finally an HOC
handleClose = () => {
this.props.history.goBack();
};
render() {
const actions = [
<FlatButton label="Cancel" primary={true} onClick={this.handleClose} />
];
return (
<Dialog>
<WrappedComponent/>
</Dialog>
)
}
We need to provide edit prop for this resource in App.js
edit={DialogUserEdit}

Firestack: firestack.auth.signInWithProvider('facebook', token, '') not firing

I wouldn't consider myself a bad developer, but 2 days into trying to get facebook login working with react native and firebase is making me think otherwise!
I have started again from scratch and am trying to implement react-native-firestack. Unfortunately I have hit a road block just trying to get the package setup.
The firestack events seem to work fine (e.g. console.logs from firestack.auth.getCurrentUser() seem to work fine - in that they fire).
Unfortunately the signInWithProvider event doesn't seem to fire. the console.log()s just before it fire, with the correct data being passed. I am hoping I have just missed some little detail, I have tried to copy all the code directly from the relevant tutorials from react-native-firestack and react-native-facebook-login (the login one looks to be running well).
See below for my code:
import React, { Component } from 'react';
import {FBLogin, FBLoginManager} from 'react-native-facebook-login';
import {
AppRegistry,
StyleSheet,
Text,
View
} from 'react-native';
import Firestack from 'react-native-firestack';
import FBLoginView from './src/FBLoginView';
export default class appTest extends Component {
render() {
const firestack = new Firestack();
firestack.auth.listenForAuth((evt) => {
if (!evt.authenticated) {
// There was an error or there is no user
console.log(evt.error);
} else {
// evt.user contains the user details
console.log('User details', evt.user);
}
})
.then(() => console.log('Listening for authentication changes'));
return (
<View style={styles.container}>
<FBLogin
buttonView={<FBLoginView />}
ref={(fbLogin) => { this.fbLogin = fbLogin }}
loginBehavior={FBLoginManager.LoginBehaviors.Native}
permissions={["email","user_friends"]}
onLogin={function(data){
console.log("Logged in!");
let token = data.credentials.token;
firestack.auth.signInWithProvider('facebook', token, '')
.then((user) => {
console.log(user)
}).catch((err) => {
console.error('User signin error', err);
});
}}
onLoginFound={function(e){console.log(e)}}
onLoginNotFound={function(e){console.log(e)}}
onLogout={function(e){console.log(e)}}
onCancel={function(e){console.log(e)}}
onPermissionsMissing={function(e){console.log(e)}}
/>
</View>
);
}
}
AppRegistry.registerComponent('appTest', () => appTest);

react-router > redirect does not work

I have searched on the internet for this topic and I have found many different answer but they just do not work.
I want to make a real redirect with react-router to the '/' path from code. The browserHistory.push('/') code only changes the url in the web browser but the view is not refreshed by browser. I need to hit a refresh manually to see the requested content.
'window.location = 'http://web.example.com:8080/myapp/'' works perfectly but i do not want to hardcode the full uri in my javascript code.
Could you please provide me a working solution?
I use react ^15.1.0 and react-router ^2.4.1.
My full example:
export default class Logout extends React.Component {
handleLogoutClick() {
console.info('Logging off...');
auth.logout(this.doRedirect());
};
doRedirect() {
console.info('redirecting...');
//window.location = 'http://web.example.com:8080/myapp/';
browserHistory.push('/')
}
render() {
return (
<div style={style.text}>
<h3>Are you sure that you want to log off?</h3>
<Button bsStyle="primary" onClick={this.handleLogoutClick.bind(this)}>Yes</Button>
</div>
);
}
}
You can use router.push() instead of using the history. To do so, you can use the context or the withRouter HoC, which is better than using the context directly:
import { withRouter } from 'react-router';
class Logout extends React.Component {
handleLogoutClick() {
console.info('Logging off...');
auth.logout(this.doRedirect());
};
doRedirect() {
this.props.router.push('/') // use the router's push to redirect
}
render() {
return (
<div style={style.text}>
<h3>Are you sure that you want to log off?</h3>
<Button bsStyle="primary" onClick={this.handleLogoutClick.bind(this)}>Yes</Button>
</div>
);
}
}
export default withRouter(Logout); // wrap with the withRouter HoC to inject router to the props, instead of using context
Solution:
AppHistory.js
import { createHashHistory } from 'history';
import { useRouterHistory } from 'react-router';
const appHistory = useRouterHistory(createHashHistory)({
queryKey: false
});
export default appHistory;
Then you can use appHistory from everywhere in your app.
App.js
import appHistory from './AppHistory';
...
ReactDom.render(
<Router history={appHistory} onUpdate={() => window.scrollTo(0, 0)}>
...
</Router>,
document.getElementById('root')
);
Logout.js
import React from 'react';
import appHistory from '../../AppHistory';
import auth from '../auth/Auth';
import Button from "react-bootstrap/lib/Button";
export default class Logout extends React.Component {
handleLogoutClick() {
auth.logout(this.doRedirect());
}
doRedirect() {
appHistory.push('/');
}
render() {
return (
<div style={style.text}>
<h3>Are you sure that you want to log off?</h3>
<Button bsStyle="primary" onClick={this.handleLogoutClick.bind(this)}>Yes</Button>
</div>
);
}
}
this topic helped me a lot:
Programmatically navigate using react router