Can't destroy AWS Cognito session from within React application - javascript

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

Related

Salesforce OAuth 2.0 implementation CRA

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.

OIDC client + Identity Server 4, setting max_age silent token reniew not working

I have an angular 10 application with OIDC JS client as open id connect. On browser or tab close I need to redirect the user back to the login page.
By setting max_age to the UserManager the functionality is working fine, however, silent token reniew is not working while using the application and it redirects to the login page. The token got expire.
const settings: any = await response.json();
settings.automaticSilentRenew = true;
settings.includeIdTokenInSilentRenew = true;
settings.accessTokenExpiringNotificationTime = 30; // default 60
settings.checkSessionInterval = 5000; // default 2000;
settings.silentRequestTimeout = 20000;// default: 10000
settings.monitorSession = true;
settings.loadUserInfo = true;
settings.filterProtocolClaims = true;
settings.max_age = 10;
settings.prompt="login"
this.userManager = new UserManager(settings);
public async completeSignIn(url: string): Promise<IAuthenticationResult> {
try {
await this.ensureUserManagerInitialized();
const user = await this.userManager.signinCallback(url);
this.userSubject.next(user);
return this.success(user && user.state);
} catch (error) {
console.log('There was an error signing in: ', error);
return this.error('There was an error signing in.');
}
}
While doing some search I found that prompt="login" should work, but not able to solve it. How can I achieve if the application is active the silent token reniew should work if they close the browser or tab prompt the login screen.
max_age=10 is saying "if the user interactively signed in more than 10 seconds ago then force interactive authentication". In short I don't think you want that as it effectively disables silent renewal and will cause the authorize endpoint to return error=login_required if prompt=none is specified (which it will be for silent renewal).
Using sessionStorage to store UserManager state should acheive what you want as this is tied to the browser window and will be automatically cleared up if the window/tab is closed or the browser closed.
This will not affect the user's session cookie on the IDP however so you'd still want to manually specify max_age=n or prompt=login when you are doing the interactive sign in (i.e. if no local client-side session currently exists). To do this you can pass additional params to signinRedirect rather than defining them at UserManager settings level:
await manager.signinRedirect({ prompt: "login" });
Additionally, since these params can be tampered with it's wise to also check the auth_time claim in your backend to ensure the user really did authenticate recently.
for this issue
On browser or tab close I need to redirect the user back to the login
page
you must switch from sessionStorage to localStorage,
new Oidc.UserManager({ userStore: new Oidc.WebStorageStateStore({ store: window.localStorage }) }).signinRedirectCallback().then(function () {
window.location.replace("./");});
please check this Page
For silent token refresh please check this Page
Setting only prompt and removing the max_age also solve the issue for me
settings.prompt="login"

Explicit renewal of session tokens in Firebase JS SDK

I have implemented the signin method using Firebase Auth for several providers like that:
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.LOCAL).then(() => {
let provider = new firebase.auth.GoogleAuthProvider(); // + facebook, gitHub
provider.addScope('profile');
provider.addScope('email');
firebase.auth().signInWithPopup(provider).then(result => {
// app logic here
However this code gives me 60 min lasting sessions and I want to learn how to automatically renew the current user session without being forced to login every hour.
I'm also 'listening' to the current user session state using this code.
firebase.auth().onAuthStateChanged(user => if (!user) //goto LoginPage
But it doesn't 'listen' per se, it works only when I try to navigate or update the page. So I don't know how to restrict access by the exact amount of time (e.g. 15 minutes max) using Firebase methods.
The documentation says there is a getIdToken method but I can't wrap my head around where to use this code. Should it be invoked every 60 minutes just before the expiration or it should be set at the time of login? Please give some hints or any tutorials covering this very situation.
EDIT:
Also I get this code in the console after some period of inactivity (I think less than 1 hour):
auth.esm.js:121 POST https://securetoken.googleapis.com/v1/token?key=AIza... 403
Firebase tokens are set to expire after 60 min. Then it gets refreshed for you automatically. There is no way to configure the expiration time, and you don't need to do anything special in your front-end code for that.
The only trick is that, you need to grant your application API key the permission to use the Token Service API to be able to mint a new id token for you once it's expired. This is done in the GCP console, API & Services (Credentials).
So, the code should be simple as the following
Add the user authentication state change Listener
fbAuth.onAuthStateChanged(user => {
if (user) {
// User is logged in
// Proceed with your logged in user logic
} else {
// USer is not logged in
// Redirect to the login page
}
})
Implement the login logic
fbAuth.setPersistence(firebase.auth.Auth.Persistence.LOCAL)
.then(() => {
return fbAuth.signInWithEmailAndPassword(email, password)
.then(userCredential => {
// Login success path
})
.catch(error => {
// Login error path
});
})
.catch(error => {
// Persistence setting error path
})
You can set the Authentication State Persistence before the login, depending on your use cases auth-state-persistence.
Make sure that your application API key has access to the Token Service API in the GCP console
This is under
GCP Console | APIs & Services | Credentials
Then edit the corresponding key to your deployment environment to grant the API key the access to the Token Service API.
Good luck ;)
Hello first I am gonna say sorry fro my bad english. I dont really understand firebase but i think it should work if you write something like this:
firebase.auth().setPersistence(firebase.auth.Auth.Persistence.SESSION).then(() => {
let provider = new firebase.auth.GoogleAuthProvider(); // + facebook, gitHub
provider.addScope('profile');
provider.addScope('email');
firebase.auth().signInWithPopup(provider).then(result => {
// app logic here
I meant that you should have firebase.auth.Auth.Persistence.SESSION insted of LOCAL

Using AWS Cognito can I resolve the authenticated IdentityId given a disabled unauthenticated IdentityId?

I have a JavaScript web application that supports Cognito unauthenticated identities. I'm trying to figure out how to identify the linked authenticated IdentityId for a DISABLED unauthenticated IdentityId.
First unauthenticated users are issued an IdentityId via AWS.config.credentials.get. Internally CognitoIdentityCredentials is using getId to generate a new unauthenticated IdentityId.
let unathenticatedIdentityId;
const AWS = require('aws-sdk');
AWS.config.region = region;
AWS.config.credentials = new AWS.CognitoIdentityCredentials({
IdentityPoolId
});
AWS.config.credentials.get(err => {
unathenticatedIdentityId = AWS.config.credentials.identityId;
});
Then our user authenticates to a Cognito User Pool via amazon-cognito-identity-js and the unauthenticated IdentityId changes to the authenticated IdentityId associated with their Cognito Login. The unauthenticated IdentityId is automatically marked DISABLED and is linked internally to the authenticated IdentityId.
let authenticatedIdentityId;
const { CognitoUserPool, CognitoUser, AuthenticationDetails } = require('amazon-cognito-identity-js');
const Pool = new CognitoUserPool({
UserPoolId,
ClientId,
});
const authDetails = new AuthenticationDetails({
Username,
Password,
});
const user = new CognitoUser({
Pool,
Username,
});
user.authenticateUser(authDetails, {
onSuccess: (session) => {
AWS.config.credentials.params.Logins = {
[PoolProviderName]: session.idToken.jwtToken,
};
AWS.config.credentials.expired = true;
AWS.config.credentials.refresh(err => {
authenticatedIdentityId = AWS.config.credentials.identityId;
});
},
});
I have the value for unathenticatedIdentityId and authenticatedIdentityId but I do not see a way in the AWS Cognito API's to resolve that the DISABLED unauthenticatedIdentityId has been linked to the authenticatedIdentityId. Conversely I do not see a way to identify what IdentityIds have been linked to the authenticatedIdentityId. The describeIdentity API will tell me that unauthenticatedIdentityId is DISABLED and that it has no Logins, but it does not point to the linked authenticatedIdentityId.
How can I, with only the value of the linked/DISABLED unauthenticatedIdentityId, resolve the value authenticatedIdentityId?
I have an app that uses AWS Cognito to obtain an identity id and then possibly authenticate it. The situation is a client uses the app first as unauthenticated (guest) and then logs in using Facebook, making him/herself as authenticated, and AWS preserves the given identity ID for the authenticated user, because he is a new user. Now, the problem comes, when you log out of the app and someone else wants to use this app as unauthenticated or even authenticated. Cognito will error out saying that the access to the identity ID is forbidden, because it has already been linked to the previous user's Facebook account.
The Cognito mobile SDKs have a way built in to handle this. They cache the identity id when using it, which is causing the issue you are seeing. When you log out, you'll want to clear that cache. I'm not sure which SDK you're using, but in iOS it's AWSCognitoIdentityProvider.clear() and CognitoCachingCredentialsProvider.clear() in Android. Similarly, if you're using Cognito Sync, there's a method in that client that will wipe the cached id and sync data.
Also have a look at https://aws.amazon.com/blogs/mobile/understanding-amazon-cognito-authentication/
Hope you are also following https://aws.amazon.com/blogs/mobile/using-the-amazon-cognito-credentials-provider/

AWS Cognito Missing required key 'DeviceKey' in params

Hi doing my user management using the so useful Amazon web service Cognito.
I would remember my users devices on login but when I'm calling the
cognitoUser.setDeviceStatusRemembered()
I have this error message :
Missing required key 'DeviceKey' in params
This is how I have implement it:
AuthService.login($scope.username.toLowerCase(), $scope.password)
.then(function(res) {
if ($scope.rememberMe == true)
AuthService.setRememberedDevice($scope.username);
})
My login function is well working for a long time.
I have read on this question :
AWS Cognito Identity JS: Forget/Remember/Do Not Remember Device
...that a call to the getCachedDeviceKeyAndPassword() could solve this problem but I can not figure out where to find an implementation of this method or how to use it.
I think #Ionut Trestian could know the right answer
Which enviornment are you running? If you run it in a browser, the tokens and device keys are stored in local storage, and if you run it in a server side enviornment, they are stored in memory.
I'm not sure which SDK/library you are using. With the current Amplify library, you can get the device key through the user object:
Auth.currentAuthenticatedUser({
}).then(user => {
user.getCachedDeviceKeyAndPassword(); // without this line, the deviceKey is null
console.log(user.deviceKey);
});

Categories

Resources