Limit logins via Github organizations in Firebase - authentication

I am working on an application using Firebase as the backend. I have a working implementation of GitHub authentication using Firebase. However, I want to limit my complete application (all read/write operations) to people in a specific GitHub organization.
From what I gather, it is to be done via adding Security Rules in Forge. However, the organization of a user is not part of the "basic auth data" that GitHub provides to Firebase for use. Is there any way for me to make the app available to members of a particular organization only?
Current Code:
var FB = new Firebase('https://XXXXX.firebaseio.com');
var auth = new FirebaseSimpleLogin(FB, function(error, user){
if(user==null){
auth.login('github',{rememberMe:true});
}
else{
//We are logged in
$.getJSON("https://api.github.com/user/orgs?access_token="+user.accessToken+"&callback=?", function(data){
var orgs = data.data.map(function(org){return org.login});
if(orgs.indexOf(config.github_org)>-1){
//Public Member of Chosen Github Org
//Now we fetch the data and render it
}
else{
//Throw them out
alert("Join the group "+config.github_org+" on GitHub to get access to Eon");
document.location="https://github.com/"+config.github_org;
}
})
}
});

To use FB Security Rules you'll need to compare the auth data with something in the Firebase (like a list of organizations), but it looks like you are trying to get those on the fly every time. Instead I'd recommend keeping a collection of users in your Firebase organized by their auth.uid that have organization lists you can check against:
users
uid1
...
uidX
orgs
org1
...
orgX
So when a user tries to access a given orgX you can check to see if root.child('users').child(auth.uid).child('orgs/orgX') exists in your Security Rules.

Related

Using Firestore for Survey Data - everybody can write, a few can read

I'm setting up an online survey. This survey will be anonymous - to fill it all you need to have is the survey's URL. I want to store the survey answers in Firestore, and later run scripts that retrieve the data and generate reports.
I want to set it up so that everybody can write to it, but only specific accounts that have access to the project can read the data. I've set up the following rules:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if request.auth.uid != null;
allow write;
}
}
}
Now I want to create the script that reads the data, and I'm not sure - which API key should I use? Firestore automatically created a firebase-sdk-admin service account - should I use this account? There are also the Browser Key and Web client Key that were created automatically. Are those the ones to use?
What I would really want is to set up the script in a way that asks me for my Google Credentials (much like the gcloud sdk does it). That way there's no sensitive information in the script at all - if the script user logs in to Google with an account that has access to the database - it works. If it doesn't - it doesn't.
Can I do that?
You can either user Firebase Custom Claims which are basically like roles in Discord so or store UIDs of authorized users in Firestore or RTDB. Then you can write you security rules like this:
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if request.auth.uid != null;
allow write: if request.auth.token.admin == true;
}
}
}
Now only the users with "admin" claim set to true will be able to write to those documents. If you go with storing the user UIDs in a Firestore document then you can check if user's UID is present their as shown below:
allow write: get(/databases/$(database)/documents/users/$(request.auth.uid)).data.admin == true;
Regarding the Service Account, you should not use them from frontend. They grant privileged access to your Firebase resources i.e. security rules are redundant. So you usually use them with the Firebase Admin SDK in a secure server env like Cloud functions. You can create a Firebase Cloud Function and then allow only whitelisted users to invoke it. That means if unauthenticated users or someone you haven't whitelisted won't be able to invoke it.
If you want to give access to your database to a teammate, then you can add members to your project from the Firebase Console. Let me know if you have any further queries.
I think I got it. Since I want to let everybody write, and only read data from scripts run by admins and not users, I can set up the following access rules:
service cloud.firestore {
match /databases/{database}/documents {
match /{document=**} {
allow read: if false;
allow write;
}
}
}
For reading, my scripts will authenticate as a service account (keeping the service account json file a secret), and will be able to read the data properly.

Disable Multiple login at Keycloak

It is possible to disable multiple login at Keycloak?
Example:
I logged in on my browser at my PC and I do a login on my mobile phone... at this moment, keycloak invalidade the token of my other browser.
It is possible?
Update:
This feature has been implemented in keycloak version 17.
See: https://github.com/keycloak/keycloak/issues/10077
Old workaround:
Keycloak does not have this feature implemented yet. If you want you can create your own custom authenticator, for that you should have sound knowledge of keycloak codebase. I was facing the same issue and came up with a workaround.
Keycloak maintains active sessions of a user as shown below:
Every time a user logs in through different device, a session is added in the above list, we can use the above info to limit the user session to one.
I'm heavily making use of keycloak admin rest APIs to get the session-related data and to delete the session: link here
Make an API request to your server when a user logs in:
client.js
componentDidMount(){
let auth = await keycloak.init({onLoad: "check-sso"});
if(auth){
const {data} = await axios.get(`/check_session/${keycloak.subject}`);
this.setState({isSessionValid: data.isSessionValid})
}
}
render(){
return(
{this.state.isSessionValid ?
<YourComponent/> :
<div> You are already logged in!</div>
}
)
}
server.js
const baseUrl = 'http://localhost/auth';
const realm = 'myRealm'
// check session of user with same credentials from different devices
export const checkUserSession = async userId => {
// initialize access token
await initializeAccessToken(baseUrl); // check admin rest api docs of keycloak
// get all active sessions of user
const sessions = await getUserActiveSessions(baseUrl, realm, userId); // check admin rest api docs of keycloak
// check if any session exists
if (sessions && sessions.length > 1) {
// sort with start time in descending order
sessions.sort((a, b) => b.start - a.start);
const currentSession = sessions[0]; // current logged in user's session
const previousSession = sessions[1]; // previously active user's session
/* I'm preventing current user to log in, for your case you can delete
the old session of this user here */
await deleteUserSession(baseUrl, realm, currentSession.id); // check admin rest api docs of keycloak
return { isSessionValid: false, sessionData:previousSession };
}
// user logged in first time
return { isSessionValid: true };
};
Looks like a lot of Keycloak users are interested in this feature so I am posting an answer which I found on my conducted research, this feature implemented by unofficial developer and here is what he shared
You can get the authenticators from my cloned branch:
https://github.com/mfdewit/keycloak/tree/KEYCLOAK-849-configurable-session-limits/services/src/main/java/org/keycloak/authentication/authenticators/sessionlimits
I've created 2 authenticators:
`RealmSessionLimitsAuthenticator`: limits the total number of sessions for a realm.
`UserSessionLimitsAuthenticator`: limits the number of session per user account and per client.
For now I advise you to copy them (including the factories) to your own project and make sure they are referenced in META-INF/services.
Also, change the AuthenticationFlowError.SESSION_LIMIT_EXCEEDED error to some value that exists in your Keycloak version. SESSION_LIMIT_EXCEEDED is added by me and only exists on my feature branch.
The downside is that you cannot provide the user proper feedback in case his session is denied.
If you have any questions on how these should be used, let me know!
Regarding the status of this task: I still have to write the integration tests before I can submit the merge request. But I'm planning to finish this soon. If anyone want to help writing these tests, I'd be grateful.
Still keycloak official JIRA i didn't find if they incorporated this feature in any recent version of Keycloak
Source
https://github.com/mfdewit/keycloak/tree/KEYCLOAK-849-configurable-session-limits/services/src/main/java/org/keycloak/authentication/authenticators/sessionlimits
These feature has been implemented in keycloak 17 : https://github.com/keycloak/keycloak/issues/10077

Restrict quickblox user creation

When a user registers at my website I also need to create a quickblox user. I got this procedure to work:
Get a session token from API route "api.quickblox.com/session.json". But to do so I need a user. So I use my own login to quickblox dashboard.
Create the user by hitting API route "/users.json". Fair eanough, it works!
Questions
It feels strange using my own dashboard login as the token generator account. Is there not some other way?
The newly created user can create more users! That is, I created a new token with the details from the just created user, whom in turn was allowed to create further users. This cant be right? What am I doing wrong?
Quickblox chat example available here: http://quickblox.github.io/quickblox-javascript-sdk/samples/chat/# serves the keys and secret in the open, like this:
QBApp = { appId: 46126, authKey: 'Nyc2fwgg8QeFJpS', authSecret: SUv3grsaDMx5'}; Is that the way to do it? Then atleast I would like to control the creation of new users..
Thanks!
Hope you doing well !!
For Quickblox must have manage session for each and every users,you have to download and integrate quickblox SDK into your web app.
You have to follow this steps:
1) Create a app on Quickblox and add credentials into your javascript SDK or configure your javascript SDK.
2) Whenever user is logged-in to you app you must have to login quickblox as well and get session for logged-in user.
3) Do whatever operation with user created session like (chat,viedo call, audio call)
4) When user log-out from your, you have to manage like user also logout form quickblox and destroy previously created sessions.
5) When registering new user you have to also register that user into your quickblox app.
this way you have to manage user management.
hope this will help you.
To create a user with an arbetary session/token this function can be used (for JS SDK)
var params = {login: 'test3', password: '12345678'};
function createUser(params) {
QB.createSession(function(err, result) {
QB.users.create(params, function(err, user) {
if (user) {
// user - JS obejct with QB user
alert("successfully created a user!");
} else {
alert("error!");
}
});
});
}
"The newly created user can create more users!"
After further research I have concuded this is a part of the design and not to worry about. I will add a job to clean up any users created by spammers.

Firebase Auth linking anonymous auth user with custom auth user

So I saw here: https://firebase.google.com/docs/auth/web/account-linking#link-auth-provider-credentials-to-a-user-account That it is now possible to link user accounts in Firebase. I also saw that Firebase provides the functionality of anonymous authentication, where it creates a user session for a user, without any credentials.
In our application we normally use CustomAuthentication with Firebase, as we have our own authentication service. This works perfectly, and we are able to use the same Auth system in-between systems that use Firebase, and the ones that don't.
Now we got to the point where we wanted to take advantage of the anonymous authentication of Firebase, to allow users to use the apps without registering, and just transfer their details after they log in. So I thought that account linking is what I need. But I can't find a way to link an anonymous account with a custom authentication account. Is something like this possible with Firebase?
I have not found linking between anonymous and custom signIns too, so I just use signInAnonymously and pass it's uid to signInWithCustomToken.
Step 1.
To be able acheive this, you should grab an uid from signInAnonymously function like this:
var uid;
auth().signInAnonymously().then(user => {
uid = user.uid;
});
Step 2.
Save current user data like this:
database().ref(`users/${uid}`).set({ some: 'data' });
Step 3.
Send this uid to your server which returns custom token. On your server you just pass this uid to firebase.auth().createCustomToken(uid) function and send this custom token back. It will contain the uid.
Step 4.
When you receive custom token, you can sign in with it like this:
auth().signInWithCustomToken(authKey);
And that's it. You are signed in with your custom token and can access your anonymous user data saved on the step 2.
I had a similar problem but I believe Firebase does not allow this type of linking because it would require linking two separate firebase user ids: the id of the custom token created user and the id of the anonymous user.
Firebase docs say:
Users are identifiable by the same Firebase user ID regardless of the authentication provider they used to sign in.
So handling linking between two user ID's would cause inconsistencies.
I got around this problem by doing a merge of the data between the two accounts.
For example (from Firebase docs):
// Get reference to the currently signed-in user
var prevUser = auth.currentUser;
// Sign in user with another account
auth.signInWithCredential(credential).then(function(user) {
console.log("Sign In Success", user);
var currentUser = user;
// Merge prevUser and currentUser accounts and data
// ...
}, function(error) {
console.log("Sign In Error", error);
});
Caveat: I've never done this. Have you seen this page? Down at the bottom, under the heading "Email-password sign-in", it lists this code fragment:
var credential = firebase.auth.EmailPasswordAuthProvider.credential(email, password);
You can then (apparently) link the credential like this:
auth.currentUser.link(credential).then(function(user) {
console.log("Anonymous account successfully upgraded", user);
}, function(error) {
console.log("Error upgrading anonymous account", error);
});

MVC4 RegisterGoogleClient to request additional permissions

I am very new to MVC, really liking it just getting used to everything.
One thing i would like to implement is the google log in (done) with ability to use the response as a way to get to the calender and the contacts API.
I am trying to find something that would speak to providing an API key with the RegisterGoogleClient method found in the AuthConfi.cs file.
I have registered an app with google and have the key that would request permissions for the calender and contacts. just cant seem to find the missing link for including this key with the RegisterGoogleClien.
Ideally i will be able to get the key passed in, user will see request for permissions to the additional items and then the OAuth key provided will be usable with the GData NuGet packages.
Thanks in advance!
ps- i am guessing i need to use the overload that allows for passing in a dictionary but can not find any documentation on that as well, probably just looking in the wrong place but i have been looking for a few days.
At the moment I have the same problem as yours. I can authenticate the user (log in with google), but I cannot retrieve the user contacts. The access_token that is returned from gmail is not valid for accessing the user contacts. I need to authenticate the user, and in the same time to send my client_id and secret of the application that is registered at the google console. Then with that access_token probably I will be able to retrieve the user contacts.
RequestSettings r = new RequestSettings(app, access_token); // how to get the access_token ???
//if i have new RequestSettings(app, user_email, password); it works
ContactsRequest cRequest = new ContactsRequest(r);
Feed<Contact> feedContacts = cRequest.GetContacts(user.email);
List<string> list = new List<string>();
foreach (Contact gmailAddresses in feedContacts.Entries)
{
// Looping to read email addresses
foreach (EMail emailId in gmailAddresses.Emails)
{
list.Add(emailId.Address);
}
}