EDIT 1
One thing I didn't make clear in my use case was that the browser DOES NOT need to show the UX to authenticate, or at least it's optional.
var credential = await navigator.credentials.get({
password: true,
mediation: "silent",
federated: {providers: federatedProviderURL}}
);
If the user has logged off or removed passwords from the UA then it will fail (credential == null).
The credential spec writers may stipulate that, if called from ServiceWorker, mediation must be "silent" or, if otherwise and no UI available an: -
Error - NOUI Action requires UI to complete
But the important point here is in the vast majority of cases keyless re-authentication can take place.
Does that change things at all?
Cheers Richard
ORIGINAL POST: -
If a Fetch in my ServiceWorker receives a 401 from the server how do I re-authenticate with the server if I have no focused or foregrounded client?
NB: I'm talking about POST requests updating the server and not just reading from cache until the network is back.
Bring the client back into focus? Scary for user with no action causing that reaction and they may not be there to login again anyway.
What does Background-Synch do if it gets a 401?
If navigator.credentials was surfaced in a ServiceWorker that would be enough!
Sessions that never expire?
What are other people doing?
Yet again I'm banned from W3C/IETF Github :-(
If someone could add the following to ServiceWorker issues that would help: -
Please see Use-Case
If a User Session has expired a ServiceWorker currently has no mechanisms available to re-authenticate with the server as there is no heuristic mechanism available for determining credentials.
If the credentials.get() was available then re-authentication could take place transparently. If federated (say Google) then if the user had logged out then that state would be honoured.
It seems there has been discussion on this before. Please see GitHub
I think that background re-authenticating should be infrequent enough that a notification of the sign-in or failure is an appropriate and user-friendly solution.
Please comment over there if you have any ideas!
Related
I'm working on a login system (the current code refers to changing the password as an example but is relative);
An example of the code using axios for changing a password;
(please do not comment on the current localStorage code, I am aware of its flaws. this is testing purposes and this question strictly relates to the password)
axios.put(
'http://localhost:5001/auth/changePassword',
{
currentPassword: password.currentPassword,
newPassword: password.newPassword,
},
{
headers: { accessToken: localStorage.getItem('accessToken') },
}
)
.then((response) => {
if (response.data.error) {
alert(response.data.error)
} else {
alert(response.data.message)
}
})
If i click the request tab I can see the password in plaintext, this is kind of jarring , is there no way somebody unauthorised could see this in a similar fashion?. I have hashing on the server side no problem and auth tokens etc etc. But being able to see the password so plain like this ..... is this actually a problem ?
TL:DR; No, you can't hide it, and No, it's not problematic.*
Part 1: Devtools and the Network Tab
Chrome is making those requests, from your device. That much is indisputable, as that's just how the internet works. It makes sense that an end user should be able to see the fields submitted. You could obfuscate the plaintext in some way, but it's not particularly problematic.
It's unlikely that this presents a significant risk to an end user. As you mentioned in your comment, the devtools window also has to be open to log the request in the first place.
Part 2: HTTPS - A Quick Summary
A majority of browsing is done with HTTPS: Hypertext Transfer Protocol (Secure). HTTPS cryptographically secures the entire conversation between your device and the intended server, meaning no man-in-the-middle can read the plaintext of your traffic without some significant trickery (most of which will require some level access to your device first.)
It's not perfect, of course, but it's almost certainly safe to say that the larger risk is posed by your machine itself - which is easier to compromise, which could give an attacker access to these credentials.
Note: I'm not an expert, and this is a heavy simplification of the process. I'd suggest doing some reading into the protocols themselves if you want a better understanding of what's going on behind-the-scenes.
* Except in very specific conditions.
I recently moved from the deprecated gapi.auth2 to the new Google Identity Services, using the javascript client library, and noticed a big difference: if someone signs in, and then reloads the page, the session is lost, and has to sign in again, every time the page is loaded. This was not the case with the deprecated library.
The problem can be easily reproduced with the Calendar API example.
Is there any configuration option to keep the session persistent? Or do I need to store the access tokens somehow? I could not find anything relevant in the official docs.
UPDATE:
The migration guide states the following:
Previously, Google Sign-In helped you to manage user signed-in status using:
Callback handlers for Monitoring the user's session state.
Listeners for events and changes to signed-in status for a user's Google Account.
You are responsible for managing sign-in state and user sessions to your web app.
However there's absolutely no information on what needs to be done.
UPDATE 2
To be more specific, the actual issue is not making the session persistent. Managing the sign in state and user session is something I can solve.
The real problem is the access token used to call the Google APIs.
As mentioned in the comments, the access tokens are 1) short lived 2) are not stored anywhere, so even if not expired, they do not persist between page reloads.
Google provides the requestAccessToken method for this, however even if I specify prompt: '', it opens the sign-in popup. If I also specify the hint option with the signed in user's email address, than the popup opens, displays a loading animation briefly, and closes without user interaction. I could live with this, however this only works if triggered by a user interaction, otherwise the browser blocks the popup window, meaning that I cannot renew the token without user interaction, e.g. on page load. Any tips to solve this?
I faced all the same issues you described in your question.
In order to help:
Google 3P Authorization JavaScript Library: in this link we can check all the methods the new library has (it does not refresh token, etc..)
This doc says the library won't control the cookies to keep the state anymore.
Solution
Firstly I need to thanks #Sam O'Riil answer.
As Sam described: "you can somehow save access token and use it to speed-up things after page reload."
Given the the Google's exampe, we should call initTokenClient in order to configure the Google Auth and the requestAccessToken to popup the auth:
tokenClient = google.accounts.oauth2.initTokenClient({
client_id: 'YOUR_CLIENT_ID',
scope: 'https://www.googleapis.com/auth/calendar.readonly',
prompt: 'consent',
callback: tokenCallback
});
tokenClient.requestAccessToken({prompt: ''})
In your tokenCallback you can save the credentials you get somehow, e.g.:
const tokenCallback(credentials) => {
// save here the credentials using localStorage or cookies or whatever you want to.
}
Finally, when you restart/reload your application and you initialize the gapi.server again, you only need to get the credentials again and set token to gapi, like:
gapi.load('client', function() {
gapi.client.init({}).then(function() {
let credentials = // get your credentials from where you saved it
credentials = JSON.parse(credentials); // parse it if you got it as string
gapi.client.setToken(credentials);
... continue you app ...
}).catch(function(err) {
// do catch...
});
});
Doing it, your application will work after the reload. I know it could not be the best solution, but seeing what you have and the library offers, I think that's you can do.
p.s.: the token expires after 1 hour and there is no refresh token (using the implicit flow) so, you will have to ask the user to sign-in again.
I have an API created by one of my team :),
And he made an endpoints "Register/Login"
his thought
When user create a user we save his data and the endpoint response it " without generating a Token"
so i can't navigate him to other screens cuz I make a request based on his Token,
So he wants me to navigate user after register to the login screen then Login endpoint will response the Token
But I think it's not a nice way and not improve UX.
So what you think we do?
generate Token in the register or log in?
The way I see this:
Solution 1:
You have him change the register API so that returns a token for you and you keep doing whatever you do with it.
Solution 2:
By registering, I'm assuming they type in a username/email, some personal details and a password!? So you have all the data to log the user in after registration. Upon successful registration, use the same username/email and password from memory (do not store them in browser storage) and call the login api to get the token (you only redirect after you've gotten the token) - so UX doesn't suffer here.
P.S. Instead of "fighting" one another over who's solution is better, try to work together in a solution. This is clearly an "I told you so" attempt - hence why I gave you two solution where both sides can do the work. Both of you can implement a solution without affecting UX, it's a matter of who's more stubborn :P
I'm building a Slack integration that is intended to modify some text and then post it to a Slack channel as though the user who triggered the command had said it.
e.g. /makeFace disapproval
#Ben 3:45pm
ಠ_ಠ
I ask for the client permission scope, which adds the chat:write:user permission. But when I hit the chat.postMessage endpoint, it only seems to allow you to post as the user who added the integration because the token it returns seems to be individuated for that user.
I know that giphy, for instance, sends its gif messages as though you are the originator, but I can't find out how they manage it. Is there any documentation for sending messages as other members of the team?
There are 2 ways to achieve this:
A. Overwriting username and icon
When you send a message with chat.postMessage it is possible to set a user name with the property username. The message will then appear as being send by that user (same for icon with icon_url).
However, this is not meant to impersonate real users, so even if you use the same username and icon as the real user the message will have the app tag, so that they can be distinguished from a real user.
Here is an example how it looks like (from a gamer Slack about flying and killing space ships):
But depending on what your requirements are that might work for you.
If you want to use it make sure to also set the as_user property to false (yes, really) and it will not work with a bot token, only with a user token.
See here for more details on how it works.
This also works for the legacy version of Incoming Webhooks, not with the current version of incoming webhooks though. (You can still get the legacy version, see this answer)
B. Having the user's token
Another approach is to always use the token from the respective user for sending the message. In combination with as_user = true messages sent by your app will look exactly as if they would come from the respective user (no APP tag).
To make that happen your app would need to collect tokens from all users on your workspace and store them for later use. This can be done by asking every user to install your app (called adding a "configuration") through the Oauth process (same you use to install your app to a workspace), which allows your app to collect and store those tokens for later use.
Update: This doesn't work. It impersonates the user who installed the app, so it merely seems to work... until another user tries to use it (and they end up impersonating you).
Go to your App's management page. Select "OAuth & Permissions".
Add the chat.write OAuth Scope to your app as a User Token Scope, not a Bot Token scope.
Take note of your User OAuth Token at the top of this page (not your But User OAuth Token).
Call chat.postMessage with
username = user id of the user you'd like to post on behalf of
token = the token from step 3. above
The resulting post will be 100% impersonated. Not just the name and icon as mentioned in other answers, but it'll 100% function as if it came from the user.
I hope this will help those who are still facing this issue.
First give the chat:write and chat:write.customize scope to your bot. The scope chat:write.customize Send messages as #your_slack_app with a customized username and avatar
From "OAuth & Permissions" settings get the bot OAuth token or even bot access token (both will work).
Then set the arguments like the following.
username to specify the username for the published message.
icon_url to specify a URL to an image to use as the profile photo alongside the message.
icon_emoji to specify an emoji (using colon shortcodes, eg. :white_check_mark:) to use as the profile photo alongside the message.
You can visit the docs from here
Most people who are trying to login with Facebook are having no trouble, but about 5% of people who try to sign up are unable to. I believe the issue is due to some people not having certain information in the response that I get back from Facebook. Why do some people not have email and location in their response even if I have requested the email and user_location permissions?
I looked at your javascript and saw a couple of things you should check into:
When you initialize the javascript SDK you set cookie=false and status=false. Most examples I've seen set these to true. This might not have anything to do with the 5% exceptions you are seeing, but I don't see how it would hurt to set them to true, and it might help. Give it a try and see if it makes a difference.
The bigger issue I see is your code is assuming that the permissions are granted. You really need to check the response object in the callback to FB.login. The user might not have logged in, and they might have denied some of the permissions. You need to check the response object to see if it has an error in it.
You will need to query the permissions table (fql) or do a get to /me/permissions to know what permissions they granted. It would be nice if the FB.login callback response object told you what was granted vs. denied, but it doesn't.
Also I believe there are cases where people don't have an email registered with Facebook or a location.
To summarize: you cannot trust that you will get what you ask for, you need to check the response object and handle the exceptions gracefully, reprompting for authentication with a message indicating why you are re-prompting.
Another thing you might want to consider adding is a client-side logging tool such as log4javascript (or roll your own) that gathers more information about these exceptions. There may be a pattern for example a specific browser that is not handling things well.