Salesforce OAuth 2.0 implementation CRA - javascript

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.

Related

Passing information from a child window for auth change?

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?

Can't destroy AWS Cognito session from within React application

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

Firebase redirect user to a page after clicking verification link

I'm using firebase in my web app with Ionic and I would like to redirect the user to a specific page (the login page, in my case) after he clicks on the link in the verification email.
At the moment, when the user clicks on the verification link, he is redirected on another browser page that says that he has confirmed the email.
I would like to redirect him directly to a page of my web app, without passing through that confirmation.
Is it possible to do that?
Yes it's possible. Look in the firebase.console on the left for "Authentication".
Then find "Templates" and look for "Email address verification". I think it's default opened. On the template you will see a small pencil, click on it. After that you can change your template however you want. At the bottom you will find the link "customize action URL". Open this link, paste your URL in the modal window and save. That's it.
I couldn't get any of the solutions here to work. So after looking up the docs I found that you add a continue URL after the email verification to which the firebase will redirect to after confirming. https://firebase.google.com/docs/auth/web/passing-state-in-email-actions
After email is verified you might also have to force refresh the firebase token if you use email_verified in your firebase rules.
You can do what I did.
Pass ActionCodeSettings to sendEmailVerification.
auth.createUserWithEmailAndPassword(email, password)
.then((res) => {
res.user.sendEmailVerification({
url: "https://example.com/path?confirm_email=true",
});
return createUser({ email, uid: res.user.uid, name });
})
This will make firebase redirect to https://example.com/path?confirm_email=true after email verification. The confirm_email is so that I know when to force refresh the firebase token.
(Optional)
Once redirected to the page you want you can check for the confirm_email param and force refresh the token accordingly.
const urlParams = new URLSearchParams(window.location.search);
const isConfirmingEmail = urlParams.get('confirm_email');
auth.currentUser.getIdToken(!!isConfirmingEmail).then(() => {
// Refreshed Token
})
Here is my code that helped me. I am only providing you necessary code that you need You have to fill rest of the code by yourself 😁.
Firebase v9 example
import React from "react";
import {
createUserWithEmailAndPassword,
sendEmailVerification
} from "#firebase/auth";
import {useHistory} from "react-router-dom"
export default function SignUp() {
const history = useHistory()
async function submit(e) {
e.preventDefault() // Prevent default.
// Sign your user using createUserWithEmailAndPassword
// Provide user's email and password
await createUserWithEmailAndPassword(email, password);
// Send verification email.
await sendEmailVerification(userCredentials.user);
let interval = setInterval(async() => {
if (userCredentials.user.emailVerified) {
clearInterval(interval);
history.push("/desired-link");
}
await userCredentials.user.reload();
}, 2000);
}
return < > { /* your sign up form */ } < />;
}
Firebase v8 code:
import React from "react";
import firebase from "firebase"
import {useHistory} from "react-router-dom"
export default function SignUp() {
const history = useHistory()
async function submit(e) {
e.preventDefault() // Prevent default.
// Sign your user using createUserWithEmailAndPassword
// Provide user's email and password
let userCredentials = await firebase.auth.createUserWithEmailAndPassword(email, password);
// Send verification email.
await userCredentials.user.sendEmailVerification()
let interval = setInterval(async () => {
if (userCredentials.user.emailVerified) {
clearInterval(interval);
history.push("/desired-link");
}
await userCredentials.user.reload();
}, 2000);
}
return <>{/* your sign up form */}</>;
}
As soon as user clicks the verification link, he will find your webpage redirects to <domain.name>/desired-link.
However, a bonus tip for you... Do not manipulate the action url. It's because by doing that, firebase.auth.currentUser.emailVerified will always return false. Meaning, user email would never be verified. It's because this action URL is responsible for email verification. So, instead of doing that, follow my example. This works for both localhost and your custom domain. Happy coding 🌹
You cannot have Firebase do a Authenticate then redirect unfortunately. The currently accepted answer on this is half-way there. By going into "Firebase -> Authentication -> Templates -> Email Address Verification -> Edit (aka. Pencil button)". You can then click on "Customize action URL" and insert any URL you want here.
Please note this URL you enter will not redirect you to the page after performing an email verification. All it will do is instead of generating the link <firebase email verification link>?mode=verifyEmail&oobCode=<code> it will instead direct you to <link you put in>?mode=verifyEmail&oobCode=<code>. Meaning that when a user lands on this page you will have to then handle the email verification yourself. Which means parsing out the query params, and sending this information along to Firebase in the back-end to verify the email in question. See the following for a potential on how to verify the email address: https://firebase.google.com/docs/reference/rest/auth#section-confirm-email-verification
More information about this can be found here: https://support.google.com/firebase/answer/7000714

Redirection to a mobile app from the webview of the app itself in React Native

I'm building an android application which requires authentication from an external auth provider.So I'm using react-native-oauth package to handle this.
The redirect_uri defined is a deep link which should ideally open my app itself after successful authentication.But the WebView seems to not handle this redirection and I'm getting response as 404-page not found.
This is the service that I have written to handle the auth:
const manager = new OAuthManager('<app_name>')
manager.addProvider({
'provider': {
auth_version: '2.0',
authorize_url:'<auth-url>',
access_token_url: '<auth-url>/token',
callback_url: 'http://localhost/provider',
}
});
manager.configure({
provider: {
client_id: '<id>',
client_secret: '<secret>',
redirect_uri: '<redirect-uri>' //DEEP LINK HERE
}
});
module.exports = {
authManager: () => {
manager.authorize('<provider>')
.then(resp => console.log(resp))
.catch(err => console.log(err));
}
}
Also I have defined my intent-filter as specified in the Android docs on how to declare the deep links for your apps.The deep link works fine when opened with Linking.openURL() from the app components.
Any help in this is much appreciated.
You can't directly set redirect_uri to your mobile app ( because most auth providers doesn't support custom OAuth scheme ).
But you can create some web page that will accept redirect from OAuth providers and will open your app ( and send all redirect params, like token ).
For example you create page https://example.com/oauth/, and set callback_url to https://example.com/oauth/XXXXX_provider, so when user will be redirected to page https://example.com/oauth/XXXXX_provider&token=xxx it will open you app using appName://example/oauth/google?token=xxx
You can handle appName://example/oauth/google?token=xxx using Deeplink ( it will open your mobile app when it is installed on device )
Example of page to handle redirects:
<html><head></head><body>
<p>Please wait while we redirect you to Your APP NAME...</p>
<p>Open appname</p>
<script>
var redirectToApp = function() {
var scheme = "appnameapp";
var openURL = "appname" + window.location.pathname + window.location.search + window.location.hash;
var iOS = /iPad|iPhone|iPod/.test(navigator.userAgent);
var Android = /Android/.test(navigator.userAgent);
var newLocation;
if (iOS) {
newLocation = scheme + ":" + openURL;
} else if (Android) {
newLocation = "intent://" + openURL + "#Intent;scheme=" + scheme + ";package=com.appnameapp;end";
} else {
newLocation = scheme + "://" + openURL;
}
console.log(newLocation)
window.location.replace(newLocation);
}
window.onload = redirectToApp;
</script>
</body></html>
WebView by default doesn't share cookies/session data with Safari/Chrome. So it is not ideal for login flow since it doesn't use the existing logged in session in Chrome/Safari.
Expo provides a WebBrowser api that will open Safari/Chrome instead of webview. Note that it opens Safari/Chrome inside the app, instead of redirecting you the browser using Linking. So users always have a button in the browser to get back to your app.
You can use WebBrowser.openAuthSessionAsync(url) to open a secure session which shares cookie/session info with the native browser in the device.
Expo also provides another api called AuthSession that simplifies a lot of boilerplate and provides a simple api.

Handling oauth2 redirect from electron (or other desktop platforms)

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
});
});

Categories

Resources