Retrieve accounts-github accesstoken - meteor

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

Related

firebase auth with MIcrosoft Graph (accessToken)

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()

Firebase refresh id tokens for server authentication

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;

How to get Meteor.user() values from backend server?

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());

Meteor retrieve Twitter credentials returning undefined

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?

How to get Meteor.user() to return on the server side?

in a file called /server/main.js (in order to ensure it is loaded last).
console.dir(Meteor.user());
Throws:
Error: Meteor.userId can only be invoked in method calls. Use this.userId in publish functions.
So I try to use, in the same file:
console.dir(this.userId);
returns:
undefined
so, not giving up, I'm thinking "that's fine I'll just read from the cookies in the header":
var connect = Npm.require('connect');
__meteor_bootstrap__.app.use(connect.query()).use(function(req, res, next) {
console.dir(req.headers);
next();
});
.... returns nothing in terms of cookies except for 'cookie: 'uvf=1''
I'm not sure what to conclude - this is senseless as I can otherwise use the Meteor.Account framework just fine, read/set user properties, etc. The server is clearly aware of the user, and the current user clearly logged in.
I'm at a complete loss, any explanation / hint / pointer would be greatly appreciated.
You have to use Meteor.user() in a place where a request is made from the client (such as a Meteor.methods or a Meteor.publish).
It can't be placed anywhere else because meteor wouldn't know at that point in the code the user is supposed to bound to. If there is a place a request of some form is made from the client it can do this:
In a Meteor.publish:
Meteor.publish("collection", function() {
//returns undefined if not logged in so check if logged in first
if(this.userId) {
var user = Meteor.users.findOne(this.userId);
//var user is the same info as would be given in Meteor.user();
}
});
In a Meteor.methods:
Meteor.methods({
"test":function() {
//should print the user details if logged in, undefined otherwise.
console.log(Meteor.user());
}
}
To use Meteor.user() on a server side route:
You need Meteor router installed as a package via meteorite to allow you to have a server rendered page. (installed via mrt install router)
A server side route could then handle the web request:
Meteor.Router.add('/awebpage', function(id) {
var userId = this.params.userid;
var logintoken = this.params.logintoken;
var isdirect = this.param.direct;
var user = Meteor.users.findOne({_id:userId,"services.resume.loginTokens.token":logintoken});
if(user) {
//the user is successfully logged in
return "You, "+user.profile.name+", are logged in!";
}
else
{
if(isdirect) {
return "<h3>Loading</h3><script>window.location.href="/awebpage?direct=true&userid="+localStorage.getItem("Meteor.userId") +"&logintoken="+localStorage.getItem("Meteor.loginToken")</script>";
}
else
{
return "Not logged in"
}
}
});
So now when you visit /awebpage it would check whether the user is logged in and do the thing you want when they are logged in. Initially there is a redirect to relay the data from localstorage back to the URI.
You can expose the userId with Meteor.publish() to global scope. Then you can use it with Meteor.Router's server side routes.
--
/server/publications.js
CurrentUserId = null;
Meteor.publish(null, function() {
CurrentUserId = this.userId;
});
-
/server/routes.js
Meteor.Router.add('/upload', 'POST', function() {
if (!CurrentUserId)
return [403, 'Forbidden'];
// proceed with upload...
});
You can use the logged in callback
Accounts.onLogin((obj)->
user = ob.user
)
Accounts.onLogin(function(obj){
var user = ob.user
})
I recently wrote a blog post describing solution to this: https://blog.hagmajer.com/server-side-routing-with-authentication-in-meteor-6625ed832a94.
You basically need to set up a server route using a https://atmospherejs.com/mhagmajer/server-router package and you can get current user with this.userId just like with Meteor methods.

Resources