What could cause specific users to have their local storage cleared, but the rest of the users are unaffected? Is it their firewalls? Is it their anti-virus? Browser? Browser version?
Any help would be greatly appreciated.
The application -
I have a website that requires login. Certain routes require the user receive a token(from the login step) if not they will be redirected to the login page.
The run down-
Two users have come forward and mentioned they couldn't log in. After some trouble shooting and looking at logs I found they were successfully logging in. So I screen shared with one user and I watched the localstorage in the dev console. I saw that they acquired a token successfully. Then they go to navigate to the myorderCards page and suddenly the token is cleared from the storage and they are routed back to the login page. This then puts them in a loop because every time they login successfully I have a return parameter which sends them back to myorderCards which then clears the token etc...
So the problem I am facing is that I can not recreate this issue. I have tested locally and on the published site. I have tested on and off network. I have tested 3 different browsers and even tested on a mac device(the two users in question are using macs) This issue only happens for the two users that have submitted a ticket. Still I can not figure out how the local storage is clearing. the only place that clears the token are the two lines you see below.
Additional Information-
I have tried changing my browser settings to have a high security setting. However, it continues to work for me. I have changed the browser settings to not allow cookies at all but that breaks the entire website.
P.S there are no errors or exceptions to go off of. Either on the server or the browser side.
app.module.ts
RouterModule.forRoot([
{ path: 'myorderCards/:id', component: MyOrderComponent,canActivate:[MyAuthGuard] },]),
In auth guard class
canActivate(route: ActivatedRouteSnapshot, state: RouterStateSnapshot): Observable<boolean> | Promise<boolean> | boolean {
const token = this.storage.getLocalStorageValue('mycurrentUser');
if (token != null && Object.keys(token).length != 0) {
const exTime = token.expires;
if (new Date(exTime.toString()) > new Date(Date.now())) {
return true;
}
else {
this.storage.clearLocalStorage('mycurrentUser');
this.router.navigate(["/mylogin"], { queryParams: { returnUrl: state.url } });
return false;
}
}
else {
this.storage.clearLocalStorage('mycurrentUser');
this.router.navigate(["/mylogin"], { queryParams: { returnUrl: state.url } });
return false;
}
}
}
Your token's expiration time comparison isn't timezone agnostic.
The issue is: If, one way or another, your token includes a time that doesn't provide its timezone assumption, like 9/13/21 5:30 PM (or epoch format not based in UTC), then it would already be expired to the end user who's 1 hour ahead of you. Etc.
As a solution, you could provide all datetimes in UTC, and even then convert it to Unix epoch to discourage people trying to manually 'read' it in their local time.
But for OAuth2, it's more common to include 'expires in' rather than 'expires at'. So the body of an OAuth2 access token is typically this:
{
"access_token":"MTQ0NjJkZmQ5OTM2NDE1ZTZjNGZmZjI3",
"token_type":"bearer",
"expires_in":3600,
"refresh_token":"IwOGYzYTlmM2YxOTQ5MGE3YmNmMDFkNTVk",
"scope":"create"
}
expires_in is how long (seconds) the token is valid/alive. When the client receives this token, they assume it expires that long from 'now' (where 'now' is subjective depending on geography). So they calculate the expiration date/time themselves and store it, which sidesteps the issue of timezone conversion altogether.
Then, for two customers authenticating at the same time, one in New York may store 6:00 PM while the one in Chicago may store 5:00 PM as an expiration date, even though they both expire the same moment.
Related
I'm having the exact same issue described in this question. But the solution doesn't work for me. The app_uninstalled webhook works perfectly fine and I'm disabling the access token upon deletion and also removing the store from the active shops list. I'm using Shopify CLI and Node.
This is what I'm doing:
Install the app
Delete the app
Click on the back button to go back to the app page. The URL format is: https://[store]/admin/apps/[random id]/?shop=[store]&host=[random hex]. I also found that this is not even hitting my app anymore (i.e. no logs after the uninstall).
I'm seeing a "There’s no page at this address" error.
Code corresponding to webhooks:
router.post("/webhooks", async (ctx) => {
try {
console.log(
`Getting called successfully. Context object: ${JSON.stringify(ctx)}`
);
let topic = ctx.get('x-shopify-topic')
if(topic && topic === `app/uninstalled`){
const shop = ctx.get('x-shopify-shop-domain')
console.warn(`App uninstalled from shop: ${shop}. Removing shop from active shopify shops list`)
delete ACTIVE_SHOPIFY_SHOPS[shop]
console.debug(`App uninstalled from shop: ${shop}. Successfully removed shop from active shopify shops list. Current list is: ${JSON.stringify(ACTIVE_SHOPIFY_SHOPS)}`)
console.warn(`App uninstalled from shop: ${shop}. Attempting to mark token as disabled...`)
const record = await getShopToken(shop, true)
console.debug(`Token record from DB for ${shop}: ${JSON.stringify(record)}`)
if(record){
await storeShopToken(record.SHOP, record.ACCESS_TOKEN, record.SCOPE, `DISABLED`)
console.debug(`Successfully disabled access token for ${shop}.`)
}else{
console.warn(`Could not find the current token entry for ${shop}. Unable to mark token as disabled.`)
}
}
await Shopify.Webhooks.Registry.process(ctx.req, ctx.res);
console.log(`Webhook processed, returned status code 200`);
} catch (error) {
console.log(`Failed to process webhook: ${error}`);
}
});
This is preventing the app from getting certified. Please advise.
If you are finding this message only on the reinstalling app cases, you need to delete the shop.shopify_token and the shop.shopify_domain fields after you receive the uninstall webhook.
What I did was to create a new uninstalled_domain column, passed the value of shop.shopify_domain to that new column after receiving the uninstall WH and then I deleted the shop.shopify_domain column.
When a user logins, I check if uninstalled_domain exists, if so, it means the user is not new, but reinstalling. Then I switch to that row in the DB and delete the just created row.
That way the user finds all his data after reinstalling.
Refresh token returned from Cognito is not a JWT token , hence cannot be decoded. Is there a way to get the refresh token expiry or it needs to be maintained at application level.
There is no way to decode a refresh token. If you know the expiration time set in cognito for refresh tokens you can store the time it was generated and calculate based on that.
just to elaborate on the accepted answer, as I had the same question.
jwt.org cannot decode the refresh token from aws, as it is encrypted
My way around it, is as follows:
the id token carries "auth_time" (see https://docs.aws.amazon.com/cognito/latest/developerguide/amazon-cognito-user-pools-using-the-id-token.html)
on login (you could technically do it on refresh as well), I look at that value and add expiration duration to that for a rough estimate
how to get the expiration duration programmatically? There are probably easier ways to do it, but the sdk-v3 command that worked for me was the 'DescribeUserPoolClientCommand' (https://docs.aws.amazon.com/AWSJavaScriptSDK/v3/latest/clients/client-cognito-identity-provider/classes/describeuserpoolclientcommand.html)
pseudo code in typescript (used in nodejs backend code) looks something like this:
import { CognitoIdentityProviderClient, DescribeUserPoolClientCommand, DescribeUserPoolClientCommandInput} from "#aws-sdk/client-cognito-identity-provider"
import get from 'lodash/get'
const client = new CognitoIdentityProviderClient({ region: [yourRegion] })
const input = {
UserPoolId: [yourUserPoolId],
ClientId: [yourWebClientId],
} as DescribeUserPoolClientCommandInput
const command = new DescribeUserPoolClientCommand(input)
const response = await client.send(command)
const refreshTokenValidityUnits = get(
response,
"UserPoolClient.TokenValidityUnits.RefreshToken"
)
const refreshTokenValidity = get(
response,
"UserPoolClient.RefreshTokenValidity"
)
// result: "days" and "30" for example
This is obviously not complete enough to get the exact values, but enough to get anyone started who, like me, might not be as familiar with the aws-sdk yet.
I'm currently working on authentication using Firebase's signInWithEmailAndPassword().
I want to check if a user logins in for the first time and recently found isNewUser.
The problem is, it always returns false because signInWithEmailAndPassword() runs in first place, making isNewUser false automatically.
Note) I don't use createuserwithemailandpassword() for registration. I manually make an account and provide it to the user.
Any suggestion?
firebase
.auth()
.signInWithEmailAndPassword(email, password)
.then(user => {
if (user.additionalUserInfo.isNewUser) {
// Want to redirect to Terms of service
// But it always returns false
}})
When you create a new user account with createUserWithEmailAndPassword () that user is immediately signed in. That is the only time when additionalUserInfo.isNewUser will be true.
I just noticed that you create the account out-of-bounds without calling the API. I'd still expect this same reason to be true though. You should be able to verify that through UserMetadata, which you can get from the User object.
When a user's password is updated I want the Security stamp value to be updated every time that happens. I believe that is how Security stamp works from my research.
I place this code in the ApplicationUserManager.cs but this isn't working:
private static string NewSecurityStamp()
{
return Guid.NewGuid().ToString();
}
What and where do I need to get the security stamp value to change each time an update is made to the user's account?
That is what happens. However, the security stamp is only re-validated on an interval (every 30 minutes, by default), to reduce the number of database queries being made. You can lower this interval, even to zero, which effectively makes the stamp be re-validated with each request. However, that will increase the chatter back and forth to the database.
services.Configure<SecurityStampValidatorOptions>(o =>
{
// WARNING: this will issue a query for every request
// You might want to rather just compromise with an interval
// less than 30 minutes (5 minutes, 10 minutes, etc.)
o.ValidationInterval = TimeSpan.Zero;
});
An alternative option is to simply log the user out after such a change. If the goal is to simply make them re-login, that should do the trick much better. Just inject SignInManager<TUser>, and then call SignOutAsync on that instance. You'll need to redirect the user afterwards. That could be directly to the sign in page or to some area of the site that is protected, which will then cause them to be taken to the sign in page to authenticate. In either case, a redirect is necessary to have the auth cookie actually be deleted.
I've recently updated some parts of the code and want to check if they play well with production database, which has different data sets for different users. But I can only access the application as my own user.
How to see the Meteor application through the eyes of another user?
UPDATE: The best way to do this is to use a method
Server side
Meteor.methods({
logmein: function(user_id_to_log_in_as) {
this.setUserId(user_id_to_log_in_as);
}
}):
Client side
Meteor.call("logmein", "<some user_id of who you want to be>");
This is kept simple for sake of clarity, feel free to place in your own security measures.
I wrote a blog post about it. But here are the details:
On the server. Add a method that only an admin can call that would change the currently logged user programatically:
Meteor.methods(
"switchUser": (username) ->
user = Meteor.users.findOne("username": username)
if user
idUser = user["_id"]
this.setUserId(idUser)
return idUser
)
On the client. Call this method with the desired username and override the user on the client as well:
Meteor.call("switchUser", "usernameNew", function(idUser) {
Meteor.userId = function() { return idUser;};
});
Refresh client to undo.
This may not be a very elegant solution but it does the trick.
Slightly updated answer from the accepted to log the client in as new user as well as on the server.
logmein: function(user_id_to_log_in_as) {
if (Meteor.isServer) {
this.setUserId(user_id_to_log_in_as);
}
if (Meteor.isClient) {
Meteor.connection.setUserId(user_id_to_log_in_as);
}
},
More info here: http://docs.meteor.com/api/methods.html#DDPCommon-MethodInvocation-setUserId