I've implemented Google Calendar API. It works well. When I allow calendar integration in my app it asks for permissions to access calendars and everything works perfectly.
However, after a week or so the permissions just disappear.
api.client.init({
apiKey: API_KEY,
clientId: CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES,
}).then((data) => {
resolve();
I can see in the network tab that the request is passing correct scopes.
But the response from Google misses the calendar scopes.
Scope sent:
scope: openid profile email https://www.googleapis.com/auth/calendar
Scope recieved:
scope: "email profile openid https://www.googleapis.com/auth/userinfo.profile https://www.googleapis.com/auth/userinfo.email"
The only way how to make it work again is to remove permissions to my app in the google account and start the whole process again.
It drives me crazy. I triple checked everything. Any ideas are hugely appreciated.
Some more info if it helps:
It's happening in my localhost. It's all allowed in Google API.
I use Google Firebase for auth process in my app.
Related
I have a SPA that's supposed to interact with IoT Core and Cloud Pub/Sub through gapi.
First I used only IoT Core. I added the necessary scope both to the app configuration under APIs & Services / Credentials and my gapi client initialization call documented here.
It worked fine, the OAuth consent screen showed the new scope, IoT Core API calls were working.
Now I want to add Cloud Pub/Sub. Again added the scope to both the app configuration and the gapi initialization call. Here's the argument to gapi.client.init:
{
apiKey: config.firebase.apiKey,
clientId: config.clientId,
discoveryDocs: [
'https://cloudiot.googleapis.com/$discovery/rest?version=v1',
'https://pubsub.googleapis.com/$discovery/rest?version=v1',
],
scope:
'https://www.googleapis.com/auth/pubsub https://www.googleapis.com/auth/cloudiot',
}
Yet, when I make a call to the Pub/Sub API, I get this response:
{
"error": {
"code": 403,
"message": "Request had insufficient authentication scopes.",
"status": "PERMISSION_DENIED"
}
}
Here's what I tried:
log out and then back in
wipe browser cache and cookies
revoke all permissions from the app in Google Accounts
Still no luck. The interesting thing is that although now both scopes are added to the app on the cloud side, after revoking app permissions and logging back in, the consent screen requests permission only for the IoT Core scope. The Pub/Sub scope doesn't show up there.
Any ideas what I'm missing?
Found the problem. The login was initiated by another part of the code that didn't have the Pub/Sub scope added.
I have a web app that needs to allow for users to book an appointment. The app loads my calander and shows a visual representation of what timeslots are available from my calander. Each timeslot available was made into a button. When the user clicks the button, it needs to set the event for that time slot. The event details are pre-made, the client doesn't do anything but click the time they want.
Ill be adding a form to accept their email and add it to the "attendees" object so that it can update their calendar.
Using the javascript quickstart, you use
gapi.load('client:auth2', () => {
gapi.client.init({
apiKey: API_KEY,
clientId: CLIENT_ID,
discoveryDocs: DISCOVERY_DOCS,
scope: SCOPES,
});
To get access to my calendar. But the rest of the quickstart
.then(() => {
gapi.auth2.getAuthInstance().isSignedIn.listen(updateSigninStatus);
});
function updateSigninStatus() {
gapi.auth2.getAuthInstance().isSignedIn.get();
}
Asks the user to sign into their own account using Oauth to add an event...
How do I just allow for them to add an event by clicking the timeslot button, without needing them to go through Oauth?
For visual representation of what it looks like:
The reason why it's asking the user to sign in is because your code is trying to make their account add a new event. What you probably want is to use a service account to be able to create the events without need to log in. This needs to be done in the backend, as it will contain the private key (which should never be public). This is called server to server application.
References & further reading
Using OAuth 2.0 for Server to Server Applications (Google's OAuth 2.0 Guide)
Google APIs Node.js Client (GitHub project)
I have an old-school angularJs app that has two pages. On both of the pages I include the auth0 lock script.
<script src="https://cdn.auth0.com/js/lock/11.9.0/lock.min.js"></script>
Of those two pages, one has the following js that specifies an auth0 lock to allow users to login:
new Auth0LockPasswordless(configuration.id,configuration.domain,
{
allowedConnections: ['email'],
passwordlessMethod: "link",
auth: {
redirectUrl: configuration.redirectUrl,
responseType: 'token id_token',
params: {
scope: 'openid profile email offline_access'
}
}
}).show();
and the other page is responsible for the call-back after they've clicked the link in their email.
var lock = new Auth0LockPasswordless(configuration.id, configuration.domain);
lock.on('authorization_error',
function(authResult) {
console.log("DEBUG::AUTHRESULT::", authResult);
});
lock.on('authenticated',
function(authResult) {
console.log("DEBUG::AUTHRESULT::", authResult);
});
Now I've set offline_access in the scope of the request, and on my local environment been prompted for additional permissions when authenticating (so it's making it through). However when I check the log from the lock.on('authenticated', function(authResult).. refreshToken is always null.
There's some conflicting documentation around the web, with both suggestions that lock will and wont return a refresh token. Is anyone able to confirm if this code should result in a valid refreshToken?
As #user44 said above in the comments, you shouldn't use a refresh token in a SPA (Single Page Application), as it's not a secure client and way to store it securely. Instead, use the silent authentication approach to request new access tokens.
https://auth0.com/docs/api-auth/tutorials/silent-authentication
Depending on which SDK you're using, either auth0-spa-js or auth0.js:
https://auth0.github.io/auth0-spa-js/classes/auth0client.html#gettokensilently
https://auth0.com/docs/libraries/auth0js/v9#polling-with-checksession-
(Disclaimer: I work at Auth0 as Sr. Solutions Engineer)
Update (07. May 2020):
It should be noted that Auth0 recently introduced Refresh Token Rotation https://auth0.com/docs/tokens/concepts/refresh-token-rotation, which is also supported by the Auth0 SPA SDK
I have a web app that uses the usual https://www.googleapis.com/auth/analytics.readonly scope on an OAuth button, and it's working fine. However, I would also like the ability for users to sign in via Google so they don't have to make a new login and password just for my app. When I implement that, I'd like to not require them to hit Login with Google, followed by another Authorize this App button - not the best user experience right out of the gate.
Is it possible to add the Google Analytics permission scope to a Google Sign-In button? If it matters, it's a Flask app with most of the stuff happening on front-end Javascript at the moment.
Need to spend more time in docs. You can easily accomplish this by adding your scope to the scope: list from Google's example:
var startApp = function() {
gapi.load('auth2', function(){
auth2 = gapi.auth2.init({
client_id: CLIENT_ID,
cookiepolicy: 'single_host_origin',
scope: 'email https://www.googleapis.com/auth/analytics.readonly'
});
attachSignin(document.getElementById('customBtn'));
});
};
The relevant doc page: https://developers.google.com/identity/sign-in/web/build-button
I am trying to return a refreshToken using the passport module of node.js.
However I am using the code below and I am unable to log any refresh token but the access token works fine.
Is there any way to specify the access-type offline for this module to hopefully return this?
var GoogleStrategy = require('passport-google-oauth').OAuth2Strategy;
passport.use(new GoogleStrategy({
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
callbackURL: "http://myurl/auth/callback"
},
function(accessToken, refreshToken, profile, done) {
console.log(refreshToken);
process.nextTick(function () {
return done(null, [{token:accessToken}, {rToken:refreshToken}, {profile:profile}]);
});
}
));
This returns the refreshToken as undefined.
Any help would be greatly appreciated.
This was solved with this link:
https://github.com/jaredhanson/passport/issues/42
specifically:
passport.authenticate('google', { scope: ['https://www.googleapis.com/auth/userinfo.profile',
'https://www.googleapis.com/auth/userinfo.email'],
accessType: 'offline', approvalPrompt: 'force' });
Jared Hanson - bless you.
All you need is accessType: 'offline' and you'll get the refreshToken on first login.
You don't need approvalPrompt or prompt in the request.
Note this only works on first login if you don't capture and save the refreshToken and associate it with a user account on first login you can't easily get it again.
If you didn't capture it the first time someone logs in, then you have two options:
If a user logs in and you don't have a refreshToken for them, you can immediately forceably log them out (e.g. by expiring their session in your app) and tell them to go to https://myaccount.google.com/permissions and revoke access to your application then just sign in again.
When they sign in again they will get the same prompt for access they got on first login and you will get the refreshToken on that first new login. Just be sure to have a method in your callback in Passport to save the refreshToken to their user profile in your database.
You can then use the refreshToken to request a rotating accessToken whenever you need to call a Google API.
You could also add both accessType: 'offline' and prompt: 'consent' options, but this is probably not what you want in a web based application.
Using these will prompt every user to approve access to your app every time they sign in. Despite the name, approvalPrompt does not enforce this, at least not in the current version of the API (judging by the number of people mentioning it and how often oAuth APIs change it's entirely possible this behavior used to be different).
This isn't a great approach for web based apps as it's not a great user experience but might be useful for development/debugging.
More about the second option:
This option is intended for scenarios such as mobile or desktop apps where the tokens will persist locally (not in a browser, where they are much more likely to be cleared when cache is cleared or they naturally expire).
Google limit how many refresh tokens they will issue for a user on an application (and they will invalidate the oldest tokens automatically and silently) so it's generally a bad idea to use this approach for a web app, as users might find they end up getting signed out of other browser sessions.