I have an auth workflow that opens a window to an external URL. The URL redirects to another URL that returns a token. I need to retrieve that token in my application.
Right now I have the following code:
const url = await retrieveAuthURL()
console.log(`Redirecting to auth url ${url}`)
if (url === null) {
console.log('Received null auth url')
return
}
window.open(url, '_blank', 'location=yes,height=700,width=600,scrollbars=0,status=1')
window.addEventListener('message',handleEvent)
where handleEvent takes the event and mutates the state of the program to set the user info based on it. But the event doesn't fire - I am guessing because a redirect, differently from a fetch, doesn't count as an event?
How do I go about retrieving the auth information I need?
Related
I am attempting to grab the authorization code appended to the redirect uri using the OAuth2.0 Web Server Flow for Web App Integration seen here: https://help.salesforce.com/s/articleView?id=sf.remoteaccess_oauth_web_server_flow.htm&type=5
When a user hits a button, I am opening a child window where the user authorizes via OAuth and is then redirected to the callback uri upon successful verification, etc. Is there a way to access the url of the child window and grab the authorization code as well as close the child window upon successful verification. My code is as follows:
const Dashboard = () => {
const openWindow = () => {
const newWindow = window.open(
`${salesforceAppUrl}/services/oauth2/authorize?&response_type=code&client_id=${clientId}&redirect_uri=https://login.salesforce.com/services/oauth2/success`,
'',
'width=850,toolbar=1,resizable=1,scrollbars=yes,height=700,top=100,left=100'
)
console.log(newWindow)
}
return <button onClick={openWindow}>test</button>
}
export default Dashboard
I am wondering if I am going about this correct way/looking for guidance on the proper way to go about grabbing the authorization code from the redirect uri when a successfully auths. Any and all suggestions would be greatly appreciated.
How do I get access to the refreshed token in Firebase Auth?
I'm building a React app, and on the signin button click run Login function. This function gives me a Google API token which I use to access google drive api.
The problem is that is token expires after an hour. This post mentions:
"If you need to know when the SDK refreshes the token in order to get a new one immediately, you should instead use onIdTokenChanged to set up a callback that will be invoked every time the user's token changes."
As such I've gone ahead and set this function up. However, how do I access this new updated token so that I can pass it along to the rest of the app?
Login Function
const login = async () => {
signInWithPopup(auth, provider)
.then((result) => {
// This gives you a Google Access Token. You can use it to access the Google API.
const credential = GoogleAuthProvider.credentialFromResult(result);
const token = credential.accessToken;
}
onIdTokenChanged
onIdTokenChanged(auth, (currentUser) => {
console.log(currentUser);
setUser(currentUser);
});
I'm trying to log out of my application that's using AWS Cognito by calling their logout endpoint. I'm not using the AWS SDK because as far as I can tell, it does not yet cover oauth app integrations and sign in using external federated identity providers (please correct me if I'm wrong about that). I log in from an AWS-hosted login screen that I'm redirected to when I call their authorization endpoint. They redirect me back to my page with a "code" which I post back to them using their token endpoint to get tokens. All of this is textbook oauth 2.0 stuff.
The problem is that when I call the logout endpoint using a JavaScript browser redirect (window.location.href = ....) it doesn't clear the cookies that are set when I logged in ("XSRF-TOKEN" and "cognito") and I can't manually clear them because they were set from the AWS domain which is different from the one where my site is hosted. The cookies do get cleared when I enter the logout link in the address bar. There's clearly a difference between using window.location.href in code and dropping a link in my address bar.
To clear out the sessoin you need to use clearCachecId() and then reset the Cognito Id credentials. This is my function using the AWS SDK:
import AWS from 'aws-sdk/global';
const getCurrentUser = () => {
const userPool = newCognitoUserPool({
UserPoolId: YOUR_USER_POOL_ID,
ClientId: YOUR_APP_CLIENT_ID
});
return userPool.getCurrentUser();
}
const signOutUser = () => {
const currentUser = getCurrentUser();
if (currentUser !== null) {
curentUser.signOut();
}
if (AWS.config.credentials) {
AWS.config.credentials.clearCachedId(); // this is the clear session
AWS.config.credentials = new AWS.CognitoIdentityCredentials({}); // this is the new instance after the clear
}
}
That should take care of that.
It's a timing issue involving the use of windows.location and cookies. It seems that I was causing the same cookie, XSRF-TOKEN, to be unset and then reset so fast that it was just not happening at all. Inserting a timeout between logging out and redirecting back to the log in screen fixes the problem. There are some guys on this thread who seem to know something about it: https://bytes.com/topic/javascript/answers/90960-window-location-cookies
Assume you are working on a front end application that performs authentication through 3rd party api. Successful authentication returns a json web token.
What would be best practices to store such token and create some sort of session for user while he is active on the website i.e. didn't close a tab or browser, however refreshing / reloading a page should not destroy such session.
Also, how can this session be used to protect routes? I am working with a stack consisting of react / redux / node / express and quiet a few other libraries. I believe I can perform certain checks within my react-router, however wouldn't it be better to do these on the express side?
You can store the token in localStorage or sessionStorage, and include it in every API request.
Local storage outlives the tab, it's stored there until you explicitly delete from it, so refreshing a page won't be a problem. Even closing a tab and then coming back won't be.
Session storage allows you to store data. Page refreshes are fine, but tab closing isn't, which is closer to the behavior you want.
As for protecting routes, the server should obviously check the token on requests to all protected API routes.
On the browser side, you will probably want to show a login form if a user tries to visit a protected route but the token isn't there (or is invalid).
With react-router, you could do it like the official repo shows in the example, via onEnter hooks: https://github.com/reactjs/react-router/blob/master/examples/auth-flow/app.js
An alternative would be to create two top-level components, one for protected routes, one for public routes (like a landing page or the sign in/sign up forms). The protected handler will then in componentWillMount check if there's a token:
- PublicHandler
+ SignIn
+ SignUp
+ Index
- ProtectedHandler
+ Dashboard
+ MoneyWithdrawal
it may looks like that , with sessionStorage (JWT token is accesseble, untill browser or tab closed)
///action creator redux
export const signupUser = creds => dispatch =>{
dispatch(requestSignup());
return API.auth.signup(creds)
.then(res => {
sessionStorage.setItem('token', res.token);// <------------------
dispatch(receiveSignup(res));
return res;
})
.catch(err => {
dispatch(SignupError(err));
);
});
};
On client : handling auth through HOC redux-auth-wrapper
On server on server you can use passport-jwt strategy
passport.use('jwt',new JwtStrategy(opts, function(jwt_payload, done) {
User.findOne({where:{ id: jwt_payload.user.id }}).then(user=>{
if (user) {
done(null, jwt_payload.user);
} else {
done(null, false);
// or you could create a new account
}
},err=>{
console.log('Error ',err);
return done(err,false);
});
}));
then just add route handler
var checkJWT = passport.authenticate('jwt')
router.get('/protected',checkJWT, (req, res) =>{
res.json(req.user);
});
You don't need sessions on server for that
This is mostly a lack of understanding of oauth2 and probably not specific to electron, however I'm trying to wrap my head around how someone would handle an oauth2 redirect url from a desktop platform, like electron?
Assuming there is no webservice setup as part of the app, how would a desktop application prompt a user for credentials against a third party oauth2 service, and then authenticate them correctly?
Electron JS runs a browser instance on your localhost. Therefore, you can handle an oauth2 redirect url by supplying a callback url of https:localhost/whatever/path/you/want. Just be sure to white list it on the oauth2 app registration page for whatever service you are using.
Example:
var authWindow = new BrowserWindow({
width: 800,
height: 600,
show: false,
'node-integration': false,
'web-security': false
});
// This is just an example url - follow the guide for whatever service you are using
var authUrl = 'https://SOMEAPI.com/authorize?{client_secret}....'
authWindow.loadURL(authUrl);
authWindow.show();
// 'will-navigate' is an event emitted when the window.location changes
// newUrl should contain the tokens you need
authWindow.webContents.on('will-navigate', function (event, newUrl) {
console.log(newUrl);
// More complex code to handle tokens goes here
});
authWindow.on('closed', function() {
authWindow = null;
});
A lot of inspiration taken from this page: http://manos.im/blog/electron-oauth-with-github/
Thank you for this solution. I also noticed that the navigate events from the webContents are not reliable when no clicks on the browser window triggers the redirection to the application redirect uri. For example Github login page would never trigger this event with the redirect URI if I was already logged in in the browser window. (It was probably using some session storage).
The workaround I found was to use WebRequest instead
const { session } = require('electron');
// my application redirect uri
const redirectUri = 'http://localhost/oauth/redirect'
// Prepare to filter only the callbacks for my redirectUri
const filter = {
urls: [redirectUri + '*']
};
// intercept all the requests for that includes my redirect uri
session.defaultSession.webRequest.onBeforeRequest(filter, function (details, callback) {
const url = details.url;
// process the callback url and get any param you need
// don't forget to let the request proceed
callback({
cancel: false
});
});