Is it possible to use Firebase as the SSO for Discourse? I’ve tried several times to get it working, but to no avail.
Has anyone been successful in using Firebase with Discourse?
Is it possible to use Firebase as the SSO for Discourse? I’ve tried several times to get it working, but to no avail.
Has anyone been successful in using Firebase with Discourse?
Can you share your code?
There’s no real code yet. I configured Discourse with the sso secret and url, but it didn’t work. I don’t really know what the url should be. Thanks the issue.
Also curious about how to do this.
Bump. Is this possible?
Did anyone figure out, how to use authentication from firebase for discourse?
Nobody found anyway to do it that I know of. I stopped looking when I didn’t get any real help. But I did figure out an alternative. All authentication is done through Firebase. If a user forgets a password or wants to change it, it is handled by Firebase. Then, if a user is able to authenticate through Firebase, I created an account in Discourse with the same user name, but with a global, secure password. Worked for my solution. You can check out the app I made for a client on Android. Search for WWJDNow in the Android Play Store. I haven’t had to do anything similar for another client yet, so I didn’t look into if further and don’t have any other example. Hope that helps.
I wanted to bump this because I’m running into the issue as well. Have there been any updates or has anyone figured it out? Otherwise I’m unsure how to pass a users Firebase info to the server for SSO.
As a follow up to myself and pinging @adam_beers and @vinothkannans, I’ve been doing a lot of research today and it seems like it may be possible using Firebase’s new ability to create and then check session cookies.
I think you would be able to store a user’s session login as a cookie when they initially login (and redirect to your main login page when they don’t have a session), and then verify that cookie inside your SSO handling if it’s passes from the request. If the cookie is verified, you could then extract the DecodedIdToken and properly send back a response for logging in.
Any thoughts on this method?
I got it working!
Hey everyone! Extremely happy to report I’ve gotten Firebase working as an SSO option for Discourse. It was as I had suggested in my last post that the key to making this work was to leverage the new ability for the Firebase SDK to set cookies on the browser after a login session.
On client-side you’ll need to do something like this:
auth.setPersistence(firebase.auth.Auth.Persistence.SESSION);
Which first sets the persistence state of your Firebase session.
Then you’ll need to tie into your users login flow with Firebase’s token logic. The implementation on our site is like this:
auth.signInWithEmailAndPassword(email, password).then(function(credential) {
credential.user.getIdToken(true).then(function(token) {
var FD = new FormData();
FD.append("idToken", token);
$.ajax({
url: 'ENDPOINTURL',
xhrFields: { withCredentials: true },
data: FD,
cache: false,
contentType: false,
processData: false,
type: 'POST'
})
.done(function(data) {
// redirect to wherever you want a logged in user to go to
location.href = "/account";
})
.fail(function(xhr) {
log('error', xhr);
});
});
This hits an endpoint on our (node/express) server, which looks like this:
app.post('ENDPOINTURL',formidable(),function(req,res) {
// Get the ID token passed
var idToken = req.fields.idToken;
// Set session expiration to 5 days.
var expiresIn = 60 * 60 * 24 * 5 * 1000;
// Create the session cookie. This will also verify the ID token in the process.
// The session cookie will have the same claims as the ID token.
// We could also choose to enforce that the ID token auth_time is recent.
auth.verifyIdToken(idToken).then(function(decodedClaims) {
// In this case, we are enforcing that the user signed in in the last 5 minutes.
if (new Date().getTime() / 1000 - decodedClaims.auth_time < 5 * 60) {
return auth.createSessionCookie(idToken, {expiresIn: expiresIn});
}
throw new Error('UNAUTHORIZED REQUEST!');
})
.then(function(sessionCookie) {
// Note httpOnly cookie will not be accessible from javascript.
// secure flag should be set to true in production.
var options = {maxAge: expiresIn, domain: 'depthkit.tv', httpOnly: false, secure: true /** false to test in localhost */};
res.cookie('session', sessionCookie, options);
res.end(JSON.stringify({status: 'success'}));
})
.catch(function(error) {
res.status(401).send('UNAUTHORIZED REQUEST!');
});
});
Note that this is mostly a reference implementation from Google’s sample here. Also, auth is our firebase admin account Auth object.
This sets the session cookie in the client browser. It’s worth noting here as well that we are using subdomains, so the intended and implemented ability is that you can login/register at depthkit.tv and then login to our forums at a subdomain using SSO.
Lastly, Discourse has to ping our server for SSO auth (triggered on clicking Login), which looks like this.
app.get('SSOENDPOINT', cookieParser(), function(req,res) {
const sessionCookie = req.cookies.session || '';
// Verify the session cookie. In this case an additional check is added to detect
// if the user's Firebase session was revoked, user deleted/disabled, etc.
auth.verifySessionCookie(sessionCookie, true /** checkRevoked */)
.then((decodedClaims) => {
//once we are here the user cookie is known to be valid and we can extract the uid
var uid = decodedClaims.uid;
var payload = req.query.sso; // fetch from incoming request
var sig = req.query.sig; // fetch from incoming request
if(sso.validate(payload, sig))
{
//valid sso, make sure the user is valid
auth.getUser(uid)
.then(function(userRecord) {
// Successfully fetched user data
var nonce = sso.getNonce(payload);
var userparams = {
// Required, will throw exception otherwise
"nonce": nonce,
"external_id": uid,
"email": userRecord.email,
// Optional - could pull these from DB values
"username": userRecord.displayName,
"name": userRecord.displayName
};
var q = sso.buildLoginString(userparams);
console.log("user authed and signed in through SSO, redirecting");
res.redirect('http://FORUMURL/session/sso_login?' + q);
})
.catch(function(error) {
console.log("Error fetching user data:", error);
console.log("redirecting to login because of invalid user data");
res.redirect('https://www.depthkit.tv/login');
});
}
else
{
console.log("unable to validate discourse payload, redirecting to login");
res.redirect('https://www.depthkit.tv/login');
}
}).catch(error => {
// Session cookie is unavailable or invalid. Force user to login.
console.log("invalied session cookie, redirecting to login");
res.redirect('https://www.depthkit.tv/login');
});
});
This is also largely pulled from Firebase’s sample implementation as well as the reference code for the discourse-sso node (sso in the code) package (thank you to the creator!!).
Learned a lot about working with the web in general when implementing this, so hopefully other people can find it useful as well!
Just wanted to thank you for sharing your code! Worked great!
Some related things in case anyone else walks down this road:
be sure to add your domain to Authorized domains in Firebase Console (Authentication -> Sign-in Method)
pay careful attention to this comment in the code above, during testing it might catch you off guard // In this case, we are enforcing that the user signed in in the last 5 minutes.
if you need to use CORS to access from a different domain you can use multiple middlewares by passing in an array: [cors(corsOptions),formidable()]
Thanks again for sharing!
I followed your code and have implemented it in cloud functions but for the discourse setup what would be the sso url
That’s the SSO route on your server, above I have it as ‘SSOENDPOINT’, but in reality it may be something like ‘/auth/v1/sso’. For cloud functions I’m not sure, but it would probably be the URL of your cloud function.
To enable in discourse admin you only need to update 3 options:
“enable sso” - checked
“sso url” - url to the /sso/discourse firebase function endpoint
“sso secret” - your-super-secret-secret (change this)
@depthkit thx for posting I was able to implement this as well.
I posted the full code here:
https://gist.github.com/tegument/f60ef0bafac80faf7522f3a29fa04435
This works perfectly in web, but in mobile web the session cookie is not set properly and it comes out empty in the discourse sso url request. Has this happened to anyone else? Any clues what it could be?
Bumping this since a lot of our users look at discussions on their phone. Is it working for mobile web for everyone else?
皆様、こんにちは。
GitHub とコメントに記載されたコードを実装しました。大半のエラー(CORS が問題でした)は解決しました。
独自の Firebase Auth を通じてログインはできるようになりましたが、セッションクッキーが app.get(/discourse,....) 経由で渡されません。デバッグ文を console.log したところ、sessionCookie が空文字に設定されていることが確認できました。その時点で req を console.log すると、req 内に「cookies」と呼ばれるものが一切存在しないことがわかります。
この原因が何か、あるいは既に解決された方がいらっしゃれば、ご教示いただけますでしょうか。
よろしくお願いいたします。
このトピックは少し古いかもしれませんが、提案があります。
NodeJS アプリケーションを作成し、Firebase Hosting に公開してください。
NodeJS 用の discourse-sss モジュールが存在します。この「アプリ」は、Firebase Auth SSO を容易にする仲介役として機能できます。
私は同様のものを作成し、ここでメタにチュートリアルを投稿してみます。
これは非常に役立ちます!
他のプラットフォームでは既に提供されているのと同様に、Firebase 用にも SSO プラグインがあると素晴らしいですね!