I'm trying to use the Twitter package to retrieve credentials for a user. The client receives the temporary token just fine, but when I use it to retrieve the real credentials, I get undefined. My code is very simple.
// Client
Twitter.requestCredential(function (tokenOrError) {
Meteor.call('register', tokenOrError)
}
// Server
Meteor.methods({
register: function(token) {
var result = Twitter.retrieveCredential(token);
console.log(result); // undefined
}
})
When I look in my mongo database, the pending credentials have correctly been stored in meteor_oauth_pendingCredentials and the key that was returned to the client is correct. When I do the same query in the database it returns the result just fine, but calling retrieveCredential from the server does not.
Any thoughts?
Links to the OAuth code:
https://github.com/meteor/meteor/blob/832e6fe44f3635cae060415d6150c0105f2bf0f6/packages/oauth/pending_credentials.js
https://github.com/meteor/meteor/blob/832e6fe44f3635cae060415d6150c0105f2bf0f6/packages/oauth/oauth_server.js
https://github.com/meteor/meteor/blob/devel/packages/twitter/twitter_server.js
My problem is similar to others that have already been asked (Meteor retrieve Twitter credentials) but the posted solution didn't work for me.
Turns out the API has changed and requires you to retrieve the credential secret on the client and send both the credential secret and token to the server.
// Client
Twitter.requestCredential(function (tokenOrError) {
var secret = Package.oauth.OAuth._retrieveCredentialSecret(tokenOrError)
Meteor.call('register', tokenOrError, secret)
}
// Server
Meteor.methods({
register: function(token, secret) {
var result = Twitter.retrieveCredential(token, secret);
console.log(result);
}
})
How can I get the credential secret in Meteor, when I do Facebook.requestCredential on the client?
Related
I'm having troubles to authenticate from Firebase Cloud Functions to a custom Node server.
Background:
I use a custom node + express server to receive calls from authenticated clients coming from our website. I can successfully verify their tokens using something like this:
const bearerHeader = req.headers['authorization'];
const bearerToken = bearerHeader.split(' ')[1];
await admin.auth().verifyIdToken( bearerToken );
What I need now:
I need to call one of this endpoints but from a Cloud Function (not from an authenticated web client).
So, on the cloud function I'm calling:
const admin = require('firebase-admin');
admin.initializeApp(); // regular initialization
const token = await admin.app().options.credential?.getAccessToken();
const config = { headers: { Authorization: `Bearer ${token.id_token}` } };
await axios.post(url, body, config);
The problem:
The token.id_token field is missing from getAccessToken():
// token:
{
access_token: 'ya29. ... ... ',
expires_in: 3599,
token_type: 'Bearer'
}
When locally run with other credentials (my user credentials when running firebase functions:shell, for example, or when setting GOOGLE_APPLICATION_CREDENTIALS to a service account credentials file) I do get an extra property id_token that I can use to authenticate. But when deployed and run on the Cloud Function, id_token property is empty.
Any ideas?
Thanks in advance.
PS: an extra problem... the local test with a service account do include id_token, but when authenticating to the server getAccessToken() fails with:
Firebase ID token has incorrect "aud" (audience) claim. Expected "<project-id>" but got "<some-hash>.apps.googleusercontent.com".
Maybe both problems solve the same way. But the server does work properly to verify user tokens coming from a website.
EDIT:
I had to exchange the access_token for an id_token, for which I followed something similar to this, with the projectId as audience.
Now the problem is about the issuer (iss) instead of the audience (aud):
Firebase ID token has incorrect "iss" (issuer) claim. Expected "https://securetoken.google.com/<project id>" but got "https://accounts.google.com".
I guess I could verify the token on the server using the same library, but then my web clients would have the same issuer problem
I am super hopeful someone can help me - I'm kind of stuck.
I'm happily using firebase auth with Microsoft AD. My AuthProvider is firebase.auth.OAuthProvider('microsoft.com').
When I call firebase.auth().signInWithPopup() with that provider, everything works GREAT. I can pick out the accessToken from the resulting UserCredential and access Microsoft Graph api's no problem (yay!).
Firebase persists and renews the authentication and my app gets the expected callback via onAuthStateChanged with the new firebase.User when the user returns to my SPA later (also yay!).
The bad news (where I'm stuck) is: how do I get the Microsoft Graph accessToken in this flow (e.g. when the user returns to my app later)? I don't want them to have to re-authenticate with another popup (yech).
Basically, how do I go from a valid firebase.User to a MS Graph accessToken when the user returns?
Thanks so so much for any help!
Firebase Auth only focuses on authentication only. They will return the OAuth access token on sign in success via UserCredential but will discard the Microsoft OAuth refresh token and not store any OAuth credential associated with the provider. So you have no way to get a new access token afterwards. If you have a good reason for Firebase Auth to manage OAuth access tokens, please file an official feature request.
UPDATE/answer: so it turns out to be simpler than I thought:
The basic idea is to authenticate (re-authenticate) using firebase and use the same clientID for silent microsoft authentication. However, you must supply a loginHint
parameter to the microsoft auth, even if you were previously authorized. loginHint can
be the email address for the firebase user...
In that scenario, the authentication is shared and you won't need to popup a second sign-in for the "microsoft half" of the process - the firebase auth works fine.
I ended up using microsoft's MSAL library (https://github.com/AzureAD/microsoft-authentication-library-for-js)... something like this:
const graphDebug = false;
const msalLogger = new Logger(msalLogCallback, { level: LogLevel.Error });
export async function graphClient(loginHint: string) {
const msal = new UserAgentApplication({
// gotcha: MUST set the redirectUri, otherwise get weird errors when msal
// tries to refresh an expired token.
auth: { clientId: CLIENT_ID, redirectUri: window.location.origin },
system: { logger: msalLogger },
// TODO: should we set cache location to session/cookie?
});
/**
* Create an authprovider for use in subsequent graph calls. Note that we use
* the `aquireTokenSilent` mechanism which works because firebase has already
* authenticated this user once, so we can share the single sign-on.
*
* In order for that to work, we must pass a `loginHint` with the user's
* email. Failure to do that is fatal.
*/
const authProvider: AuthProvider = callback => {
msal
.acquireTokenSilent({ scopes: SCOPES, loginHint })
.then(result => {
callback(null, result.accessToken);
})
.catch(err => callback(err, null));
};
const client = Client.init({
authProvider,
debugLogging: graphDebug,
});
return client;
}
When you are using signInWithPopup, the result object contains the credentials you are looking for.
firebase.auth().signInWithPopup(provider)
.then(function(result) {
// User is signed in.
// IdP data available in result.additionalUserInfo.profile.
// OAuth access token can also be retrieved:
// result.credential.accessToken
// OAuth ID token can also be retrieved:
// result.credential.idToken
})
.catch(function(error) {
// Handle error.
});
Hope this helps.
If you look deep enough you should find msal access token in firebase response under (firebaseAuth.currentUser as zzx).zzj()
I have a project using firebase where I use firebase id token to verify user on back-end.
Actually, on client, when a user login I use getIdToken(true) and attach it to my requests header.
On server for each request I use verifyIdToken to check if the user is logged in and it's valid.
The problem is that after some time the token expire and the backend is not able to verify the user.
My question is: how to get this work?
I think about using a request interceptor from client side to get an id token for each request but i don't know if this is a good practice and if I have to invalidate the other tokens too with admin.auth().revokeRefreshTokens(userId).
Thanks in advance.
client
axios.interceptors.request.use(function (config) {
if (!firebase.auth().currentUser) {
return config
}
return firebase.auth().currentUser.getIdToken(true).then(token => {
config.headers.Authentication = token
return config
})
}, function (error) {
// Do something with request error
return Promise.reject(error)
})
server
let decodedToken = await admin.auth().verifyIdToken(token);
let userId = decodedToken.uid;
I have two meteor app using database for frontend and backend. Due to some bulk operation. frontend app calls backend server. works fine in many methods. in few method I should check authentication.
frontend
let remote = DDP.connect(<url>);
remote.call('methodName',argument, function(err,res){
});
backend
Meteor.methods({
methodName: function(argument) {
Meteor.user() // null value
}
});
How secure suppose I send userId as parameter?
You have to login in a way or another.
You can do something like this:
var remote = DDP.connect(url);
result = remote.call('login', {
user: user,
password: {digest: SHA256(password), algorithm: 'sha-256' }
});
Sources:
https://forums.meteor.com/t/different-interfaces-based-on-devices/264
You can get user data on server-side by:
var userData = Meteor.users.findOne(Meteor.userId());
I'm writing a third party package that uses the GitHub API. I'm now trying to use the accessToken from the accounts-github package in order to make authenticated GitHub API requests.
How can I retrieve the accessToken from accounts-github?
If you're doing it from the server side its as follows:
var user = Meteor.user().services.github.accessToken;
On the client side its a bit tricky because the services field is not published. You can publish it if you run a publish method as follows:
Meteor.publish('account', function() {
return Meteor.users.find({_id: this.userId},{fields:{services: 1}});
});
I would recommend storing the accessToken in profile when you create the user alongwith anything else you need on the client.
Accounts.onCreateUser(function(options, user) {
if (options.profile)
user.profile = options.profile;
user.profile.github_accessToken = user.services.github.accessToken;
return user;
});
You can then access the accessToken on either the client or the server with Meteor.user().profile.github_accessToken