Why is my information passed lagging behind by one? - reactjs

I am trying to code a simple React app that requires a user to sign in with Google before sample data can be accessed in the app. I would like a welcome message to display upon login that says "Welcome, [name]!" Currently, this function is working, but it's lagging behind by one sign-in.
The beginning state of my app
This is how the app appears at the beginning.
The app after one login
This is how the app appears after one login. Note that instead of getting the name from the user's Google account, it displays the default value of "default".
The app after two or more logins to the same account
After logging out and logging back in again, however, the app functions normally, showing the welcome message with the user's name. I've tried logging in with a different account, and the lag persists, i.e. "default" displays when logging in with Naomi, "Naomi" displays when logging in with Ally, "Ally" displays after logging out and logging back into Ally.
I've tried logging the variable with the user's name (userName) to the console right before information is passed, and it appears to be firing AFTER the log-in is completed, but I'm not sure why.
The code I hope is relevant is below.
import React from 'react';
import Login from './Login';
var userName = "default";
class LoginButton extends React.Component {
// some other code here
onSignIn = googleUser => {
this.toggleLoggedIn();
let user = googleUser.getBasicProfile();
let id_token = googleUser.getAuthResponse().id_token;
userName = user.getName(); //I try to grab the name from the user's account and assign it to userName
console.log('google user obj', user);
{/*console.log('google_id_token', id_token);*/}
console.log('Name:', userName); //This properly logs the correct userName to the console
};
// some other code here
render() {
console.log(this.state.isLoggedIn);
console.log(userName); //this incorrectly logs the userName lagged by one
// noinspection CheckTagEmptyBody
return (
<div>
<Login isLoggedIn={this.state.isLoggedIn} name={userName}/> //this passes the incorrect userName
<div id="my-signin2"></div>
<br />
{this.state.isLoggedIn && (
<button style={{ width: 200, height: 40, textAlign: 'center' }} onClick={this.logout}>
Logout
</button>
)}
</div>
);
}
}
Any help would be much appreciated.

My problem was the placement of this.toggleLoggedIn();
Moving it to the end of the onSignIn function block solved my problem.
onSignIn = googleUser => {
console.log("Sign in successful");
let user = googleUser.getBasicProfile();
userName = user.getName();
this.toggleLoggedIn();
};

Related

User has to be logged in to see some text

Using Firebase v9, NPM, ReactJS
Hi everybody,
how can I display some text in order, that user is signed in?
e.g:
if(loggedIn){
<p>you are logged in</p>
} else if(notLoggedIn){
<p>you arent logged in</p>
}
Already have done Firebase auth with email and password.
You need to Set an authentication state observer and get user data
https://firebase.google.com/docs/auth/web/start#set_an_authentication_state_observer_and_get_user_data
follow this link it will get you where do you need
its somethings like that
firebase.auth().onAuthStateChanged((user) => {
if (user) {
// User is signed in, see docs for a list of available properties
// https://firebase.google.com/docs/reference/js/firebase.User
// show message user is logged in
const uid = user.uid;
// ...
} else {
// User is signed out
// show message user is not logged in
}
});

Signing A User Out of Google When They Log Out of My App

I have a React app that allows users to login to the app via signing into their Google account.
Here's my logout function. Removes the token that was stored in local storage. Pushes user to the login page, but, when the GoogleSignIn button re-renders, the button state indicates "Signed in with Google". And I want that state to say "Sign in With Google". Which would mean they were signed out in this here function:
const logOut = () => {
localStorage.removeItem('token');
//LOG USER OUT OF GOOGLE ACCOUNT AS WELL
history.push('/login');
}
So what additional code do I need to insert here?
Thanks,
Ironman

MERN How to show modal or toast ONLY when user has just logged in or registered an account

Someone new visits my web app and decides to sign up. When they sign up they are taken to the dashboard. Upon visiting the dashboard for the first time Id like to welcome them with a toast message or a modal.
I am using passport local and passport google oauth2.0. which always sends back a req.user object with properties.
I am not sure how to set up a useEffect to trigger this message when they visit the dashboard. Do I do something like
const useEffect(() => {
// show toast or modal
},[// based on cookie time? or some req.user prop?])
You can simply use LocalStorage or cookies to know if user is logged in or not then in your useEffect you can do something like this.
useEffect(()=>{
//don't store any user info in local storage only flag
const isLogin=localStorage.getItem("flag")//you can also use cookies
if (isLogin==true)
{
showModal(true);
}
},[showModal])
//in your return
return(
showModal==true?<Modal/>:<></>
)

Authentication with oidc-client.js and Identityserver4 in a React frontend

Lately I'm trying to set-up authentication using IdentityServer4 with a React client. I followed the Adding a JavaScript client tutorial (partly) of the IdentityServer documentation: https://media.readthedocs.org/pdf/identityserver4/release/identityserver4.pdf also using the Quickstart7_JavaScriptClient file.
The downside is that I'm using React as my front-end and my knowledge of React is not good enough to implement the same functionality used in the tutorial using React.
Nevertheless, I start reading up and tried to get started with it anyway. My IdentityServer project and API are set-up and seem to be working correctly (also tested with other clients).
I started by adding the oidc-client.js to my Visual Code project. Next I created a page which get's rendered at the start (named it Authentication.js) and this is the place where the Login, Call API and Logout buttons are included. This page (Authentication.js) looks as follows:
import React, { Component } from 'react';
import {login, logout, api, log} from '../../testoidc'
import {Route, Link} from 'react-router';
export default class Authentication extends Component {
constructor(props) {
super(props);
}
render() {
return (
<div>
<div>
<button id="login" onClick={() => {login()}}>Login</button>
<button id="api" onClick={() => {api()}}>Call API</button>
<button id="logout" onClick={() => {logout()}}>Logout</button>
<pre id="results"></pre>
</div>
<div>
<Route exact path="/callback" render={() => {window.location.href="callback.html"}} />
{/* {<Route path='/callback' component={callback}>callback</Route>} */}
</div>
</div>
);
}
}
In the testoidc.js file (which get's imported above) I added all the oidc functions which are used (app.js in the example projects). The route part should make the callback.html available, I have left that file as is (which is probably wrong).
The testoidc.js file contains the functions as follow:
import Oidc from 'oidc-client'
export function log() {
document.getElementById('results').innerText = '';
Array.prototype.forEach.call(arguments, function (msg) {
if (msg instanceof Error) {
msg = "Error: " + msg.message;
}
else if (typeof msg !== 'string') {
msg = JSON.stringify(msg, null, 2);
}
document.getElementById('results').innerHTML += msg + '\r\n';
});
}
var config = {
authority: "http://localhost:5000",
client_id: "js",
redirect_uri: "http://localhost:3000/callback.html",
response_type: "id_token token",
scope:"openid profile api1",
post_logout_redirect_uri : "http://localhost:3000/index.html",
};
var mgr = new Oidc.UserManager(config);
mgr.getUser().then(function (user) {
if (user) {
log("User logged in", user.profile);
}
else {
log("User not logged in");
}
});
export function login() {
mgr.signinRedirect();
}
export function api() {
mgr.getUser().then(function (user) {
var url = "http://localhost:5001/identity";
var xhr = new XMLHttpRequest();
xhr.open("GET", url);
xhr.onload = function () {
log(xhr.status, JSON.parse(xhr.responseText));
}
xhr.setRequestHeader("Authorization", "Bearer " + user.access_token);
xhr.send();
});
}
export function logout() {
mgr.signoutRedirect();
}
There are multiple things going wrong. When I click the login button, I get redirected to the login page of the identityServer (which is good). When I log in with valid credentials I'm getting redirected to my React app: http://localhost:3000/callback.html#id_token=Token
This client in the Identity project is defined as follows:
new Client
{
ClientId = "js",
ClientName = "JavaScript Client",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
// where to redirect to after login
RedirectUris = { "http://localhost:3000/callback.html" },
// where to redirect to after logout
PostLogoutRedirectUris = { "http://localhost:3000/index.html" },
AllowedCorsOrigins = { "http://localhost:3000" },
AllowedScopes =
{
IdentityServerConstants.StandardScopes.OpenId,
IdentityServerConstants.StandardScopes.Profile,
"api1"
}
}
Though, it seems the callback function is never called, it just stays on the callback url with a very long token behind it..
Also the getUser function keeps displaying 'User not logged in' after logging in and the Call API button keeps saying that there is no token. So obviously things are not working correctly. I just don't know on which points it goes wrong.
When inspecting I can see there is a token generated in the local storage:
Also when I click the logout button, I get redirected to the logout page of the Identity Host, but when I click logout there I don't get redirected to my client.
My questions are:
Am I on the right track implementing the oidc-client in combination with IdentityServer4?
Am I using the correct libraries or does react require different libraries than the oidc-client.js one.
Is there any tutorial where a react front-end is used in combination with IdentityServer4 and the oidc-client (without redux), I couldn't find any.
How / where to add the callback.html, should it be rewritten?
Could someone point me in the right direction, there are most likely more things going wrong here but at the moment I am just stuck in where to even begin.
IdentityServer4 is just a backend implementation of OIDC; so, all you need to do is implement the flow in the client using the given APIs. I don't know what oidc-client.js file is but it is most likely doing the same thing that you could have implemented yourself. The flow itself is very simple:
React app prepares the request and redirects the user to the Auth server with client_id and redirect_uri (and state, nonce)
IdentityServer checks if the client_id and redirect_uri match.
If the user is not logged in, show a login box
If a consent form is necessary (similar to when you login via Facebook/Google in some apps), show the necessary interactions
If user is authenticated and authorized, redirect the page to the redirect_uri with new parameters. In your case, you the URL will look like this: https://example.com/cb#access_token=...&id_token=...&stuff-like-nonce-and-state
Now, the React app needs to parse the URL, access the values, and store the token somewhere to be used in future requests:
Easiest way to achieve the logic is to first set a route in the router that resolves into a component that will do the logic. This component can be "invisible." It doesn't even need to render anything. You can set the route like this:
<Route path="/cb" component={AuthorizeCallback} />
Then, implement OIDC client logic in AuthorizeCallback component. In the component, you just need to parse the URL. You can use location.hash to access #access_token=...&id_token=...&stuff-like-nonce-and-state part of the URL. You can use URLSearchParams or a 3rd party library like qs. Then, just store the value in somewhere (sessionStorage, localStorage, and if possible, cookies). Anything else you do is just implementation details. For example, in one of my apps, in order to remember the active page that user was on in the app, I store the value in sessionStorage and then use the value from that storage in AuthorizeCallback to redirect the user to the proper page. So, Auth server redirects to "/cb" that resolves to AuthorizeCallback and this component redirects to the desired location (or "/" if no location was set) based on where the user is.
Also, remember that if the Authorization server's session cookie is not expired, you will not need to relogin if the token is expired or deleted. This is useful if the token is expired but it can be problematic when you log out. That's why when you log out, you need to send a request to Authorization server to delete / expire the token immediately before deleting the token from your storage.

Display logged in user's first name onto page (from database)

In preparing a welcome page after a user has logged in, I'd like the page title to display their first name which is found in the user ID database (e.g - "Welcome, Sally!"). This is a bit different than just using a cookie to relay the username in a location; like for example in the top corner to access user settings.
The site is being built with React, if that affects the code needed.
Any suggestions?
are you asking how to get the username in the app state? or just how you would render that?
if you already have the firstname in an auth object in state you could do something like this:
class WelcomePage extends Component {
render() {
const { auth } = this.props
var pageTitle = `Welcome, { auth.firstname }`
return (
<h1>{ pageTitle }</h1>
)
}
}
export default WelcomePage
otherwise I would need more information to understand what you're asking
Here is a solution I worked out that seems to do the job. This lies within the React component for the page rendering.
render: function(){
var user = JSON.parse(localStorage.getItem('user'));
...
return (
<div>
<h2>Welcome, {user.firstname1}!</h2>
</div>
Here's a quick screen shot of the result ("Candy" being the logged-in user's first name):

Resources