My Flask application is currently uses PostgreSQL to store all authentication information (users, tokens) and interact with it. For other logic I need to use MongoDB. I like to use one technologies of one type instead of multiple to reduce complexity of an application (for example, only Redis instead of Redis + memcached).
So for now I'm thinking about using MongoDB only, using it as backend for authentication process.
Current workflow is following: PostgreSQL stores two tables: User and Token. When I sign up user, I open transaction, store his data (username, login, password) in User table, then insert activation token to Token table, then send activation letter, then close transaction. So, problem occurred in storing User, Token or later in code, when I'll try to send email, transaction will be rolled back. It prevents cases when user created, but token not, so account can't be activated.
As I know, transactions is not feature of MongoDB. So, if I will have two documents, User and Token, I will not be able to rollback creating of first if second can't be created.
My questions are:
How would you implement described behavior on MongoDB?
Is it good idea to use only MongoDB for all stuff instead of PostgreSQL for authentication and MongoDB for documents?
Yes, you do have to implement the signup logic yourself in this case. For example if you store the following document:
user : {
name : 'Alex Black'
email : 'alex#example.com'
token : {
value : 'some_random_token' # subject for indexing
expires : '00.00.00.18.11.2012' # use MongoDB date here
}
}
Then e.g. a cron script should remove all the expired users. But a trick would be in using the NoSQL features of Mongo!
Just create two separate collections: Users and e.g. UnregisteredUsers. Store the user information to UnregisteredUsers and only when registration is confirmed transfer the user document from UnregisteredUsers to Users.
Related
I have created a FastAPI application which has a database with postgresql. The application uses docker, a container for the app and another for the database. Furthermore, it implements authentication with JWT tokens and encrypts passwords with bcrypt. My problem is that logically the endpoint to create new users requires authentication. How do I create the first user? That is, temporarily remove authentication from the endpoint and create the user, but I must write the step by step for project delivery and I do not consider pertinent to give that solution.
I tried to build the containers and write the steps to insert a user from the database container terminal. However the record is saved in the database but with the password unencrypted, so when I try to authenticate it doesn't work.
In summary, how to create that first user?
You should define a startup event which would populate the database with an admin account for you. This way, you don't ever have to temporarily remove authentication while still having a valid account that you can use. Here's an example of how you could do it:
from fastapi import FastAPI, Request
from domain import *
app = FastAPI()
users = {}
#app.on_event('startup')
async def populate_admin():
if "admin" not in users:
users['admin'] = {
'username': 'admin_user',
'password': hash('totally_secret_password')
}
Naturally, you would want to use an actual database for your persistance layer and an actual hash function like bcrypt. Another thing commonly implemented is to read the username and password of the "admin" user from some config file or environment variables instead of having it hard-coded in the function. Hope this helps!
Hello guys i have a query where I'm getting the user email from the web services where i want to filter the user email either it is(lower/upper case) provided by the user while login:
user = User.objects.filter(email_id=v1_api.payload['email_id']).first()
here "v1_api" is blueprint in flask-restplus this code work fine if the user enters the email address which is same as the database but its throws an exception when I write "CAPSLOCK#gmail.com" in place of "capslock#gmail.com"
I want it to make case insensitive either the email in lower/uppper it should always considered it as a lower case...
In other database like SQLAlchemy we can do this in such a way:
from sqlalchemy import func
user = models.User.query.filter(func.lower(User.username) == func.lower("GaNyE")).first()
But in cassandra I'm not able to do it please suggest me how to do this .....thanks
It's not possible to do this in Cassandra - Cassandra queries by precise value that you provided, it couldn't apply any function during the query. So you need to do the "normalization" of email when you're inserting data into database.
I am thinking about security implementation for my python web app.
For example I have user and profiles.
Each user can edit his profile by sending POST at /profile/user_id
On every request i can get session user_id and compare it with profile.user_id if they not same - raise SecurityException().
But I also can do it another more common way:
generate for each profile
secret = hash(profile_data+secret_key)
and render for auth. users links like this:
/profile/4?key=secret
So idea is to generate secret key based on editable object data and check this key on server side. If user don't know secret key it can't get links to edit other profile, and so can't modify them.
How this method of protection called?
Has it any problems in comparsion with session based user_id check?
/profile/4?key=secret
Practical issues:
it's generally a bad idea to put a secret in a URL. URLs leak easily through logs, history, referrers etc. Also it breaks navigation. It's better to put the secret in a cookie or POST data.
you would typically include a time limit in the signed data, so tokens aren't valid forever
you would typically include some kind of flag/counter/state on the user information and in the signed data, so that a user can update something (eg change password) to invalidate previously-issued tokens
you also have to ensure that the lifecycle of signed data can't be longer than that of the token, eg that you can't delete user 4 and create a new user 4 for whom the token is still valid
the hash you would use would be an HMAC using a server-side secret for the key
Signed authentication tokens are typically used as an alternative to database-backed session storage, for performance or operational reasons. It's certainly possible to do sessions securely with signed tokens, but if you're already using stored sessions for other purposes anyway you don't have a lot to gain.
I have a case where I have three sites A, B, C . A is more of administrative site, B and C are consumer facing applications. Each of them have their own separate databases. What I want to do now is as follows:
I want all three A, B, C to be connected in such a way that when a new user registers at B and when registration is successful, the same user should be able to login into A or C. If there is any change in users details they reflect on A and C as well.
What I thought as solution till now is:
When a user registers at B, I can replicate user on A and C with same credentials, so this would allow user to login into any of the three sites.
I was also pondering upon idea of having a central database to store django auth models, but I am not sure in that case how authentication would work.
I need suggestion as to how this can be done. Also, does this qualify to be called as SSO?
In case if someone finds question to be misleading or vague or inappropriate do mention as to why before downvoting
There are two solutions to your problem:
Use routing to multiple databases. Django supports different databases for different models (more info on # https://docs.djangoproject.com/en/1.8/topics/db/multi-db/). So you could route all queries for your auth models to the authentication database. Django documentation already provides such a configuration # https://docs.djangoproject.com/en/1.8/topics/db/multi-db/#an-example. I've not tested this configuration however this will have the problem of not being able to use foreign keys to your user model (since it will be stored in a different database). To resolve this, you could have a UserProxy model (or something similarly named) in all your projects that will just keep the username of the User so you'll be able to create foreign key relations from your models to the UserProxy. Any user data that would need to be retrieved would be forwarded to the correct User object by the UserProxy.
Use django authentication. Django can be configured to use a number of different authentication methods (check the docs # https://docs.djangoproject.com/en/1.8/topics/auth/customizing/). For your sites B and C you can configure an authentication backend that uses the database of the administrative site (A) to login. I am using this method succesfully - here how you could do it:
class RegUsrBackend(django.contrib.auth.backends.ModelBackend):
def authenticate(self, username=None, password=None):
try:
conn = django.db.connections['users']
cursor = conn.cursor()
cursor.execute("select pn.password, pn.username, au.first_name, au.last_name from auth_user au where au.username = %s ", [username])
row = cursor.fetchone()
if row and check_password(password, row[0]):
user, created = get_user_model().objects.get_or_create(username = row[1] )
if user.first_name != row[2] or user.last_name != row[3] :
user.first_name = row[2]
user.last_name = row[3]
user.set_unusable_password()
user.save()
return user
except:
return None
As you can see, here I've also configured a different users database (named users) and I am issuing a raw query to this database to get the user with the passed username and its password hash. After that, I check if the user exists and has the correct password (using the check_password) and if everything checks, I use get_or_create to either retrieve or create the local instance (i.e the instance of the user in the application's database) of that user. Finally, before returning the user I check to see if there's a change in his first or last name to the administrative application and update them in the local one.
Finally, you need to put this backend in the in the AUTHENTICATION_BACKENDS settings of the applications using it.
Using this method you won't have to use any UserProxy to support foreign keys (since there will exist a local User model) models but I feel that it is a more hackish method than the first one. Also, if the details of a user has been changed in the administrative database the details in the others will be updated when he logs in. Also you could change your backend to something even more exotic, for example instead of querying the database you could create a REST api in your administrative backend and use this to login to the other applications.
Finally, to answer your question about SSO, what I've described above is not SSO. SSO means that when a user logs in to a site he won't have to log in again to the others becase a single session key is kept and shared in all these sites. A common solution for SSO is to use CAS (http://jasig.github.io/cas/4.1.x/index.html) however I don't have good experience with it (and you'll need to have another, java based server to host CAS).
I am trying to wrap my head around implementing proper authentication in an SPA with a backend.
I am looking at 2 different frontends:
1. SPA with Ember.js
2. Mobile application
I am trying to design a backend that serves both (either in Rails or in Python, have not decided yet). I want the authentication to take place either via Google or Facebook, i.e. I do not need to maintain separate user registrations but I still need to maintain users as I want to be able to 'merge' authentications from Google and Facebook at the end of the day. I also want the backend to be fully stateless for scalability reasons. Also I would prefer to do as much as possible on the client side to spare the load from the backend.
My questions are as follows:
I will request an authentication token in the SPA or in the mobile app. Should I convert this to an access token on the server side or on the client side? I need to authorize backend requests and I need to do that statelessly. My idea was to do everything on the frontend side and pass along the access token to each and every backend request, to be validated on the server side as well but I am not sure if this is an efficient approach.
I want to be able to merge Google and Facebook authentications, what are the best practices for that? I was planning to maintain an user registry on the server side and check the email addresses coming from the authorized backend requests and merge the users if there is a match in email addresses. What are the best practices and are there any libraries supporting this in Python/Flask or in Ruby or Rails?
Thanks!
I'm not really sure what do you mean by 'stateless'. You obviously need some database to store user's data (otherwise you don't need backend at all). So the database is your state. HTTP protocol is stateless by definition, so you can't really be very stateful by other means than storing data in DB.
I will request an authentication token in the SPA or in the mobile app. Should I convert this to an access token on the server side or on the client side?
If you don't need to use Google/Facebook on behalf of your users (and your wording suggests that you don't), you don't need to convert auth_token to server_token at all.
You just need to call Google/Facebook API with that (Ruby has libraries for both, so it's basically a one line of code) and get social network's user ID and user email.
Then you save that ID+email in your database and give your internal server token (just random string) to your user. You generate that string yourself and give it to the client.
If user logs in from another device (i.e. it gives you auth_token with which you find out that user's email belongs to one of already-registered users), you either return existing internal token, or generate new one and bind it to the existing user (depends on what you prioritize – high security of simplicity of implementation/maintenance).
I want to be able to merge Google and Facebook authentications, what are the best practices for that?
Facebook guarantees that if it gives you user email, then it's ensured that that email belongs to the given user. Google, obviously, does the same. So you just merge them by emails.
You don't need some special libraries for that, as it is simple operation with you code on the language of your choice.
I'd organize all the things in database in the following manner:
Users table
id
email
Authentications table
user_id
email
social_uid # facebook number or google email
social_network # string, 'facebook' or 'google'
device # user agent, e.g. 'android'
ip # last login IP address
token # internal token
When user logs in, Authentication object is created. If there is no user with such email, the user is created. If there is a user, it's bind to the authentication object (both via user_id field).
Notes on access tokens
If you do plan to interact with social network (in other ways than just authenticating user), you should exchange auth_token for server_token. server_token is a 'permanent' (well, kind of) authorization token for accessing APIs of social network, whereas auth_token has a very limited lifespan (and some API calls may be restricted if you didn't obtain server_token).
Still, server_token can expire (or user can recall their access for your application), so you should plan ahead to detect that situation and re-acquire token/authorization if needed.
Key points when building Rails app
In Rails, in order to create tables, you need to write migrations:
gem install rails
rails new my_project
cd my_project
rails generate migration create_users
rails generate migration create_authentications
This will generate project folder structure and two migration files, which you need to fill in:
# db/migrate/xxx_create_users.rb
def change
create_table :users do |t|
t.string :email
end
end
# db/migrate/xxx_create_authentications.rb
def change
create_table :authentications do |t|
t.integer :user_id
t.index :user_id
t.string :social_uid
# etc., all other fields
# ...
end
end
Then you generate 'models' to handle database-related manipulations:
# app/models/user.rb
class User < ActiveRecord::Base
has_many :authentications
end
# app/models/authentication.rb
class Authentication < ActiveRecord::Base
belongs_to :user
before_create :set_token
after_commit :create_user_if_needed
private
def set_token
self.token = SecureRandom.urlsafe_base64(20)
end
def create_user_if_needed
unless self.user.present?
self.user.create(email: self.email)
end
end
end
And write 'controller' to handle request from user with a single method inside of it:
# app/controllers/login_controller.rb
class LoginController < ActionController
# Login via Facebook method
def facebook
token = params.require(:auth_token)
# we will use Koala gem
profile = Koala::Facebook::API.new(token).get_object('me', fields: 'email')
# user is created automatically by model
authentication = Authentication.where(social_network: 'facebook', social_uid: profile['id'], email: profile['email']).first_or_create
authentication.update(...) # set ip and device
render json: {token: authentication.token}
end
# This one you'll have to write yourself as an exercise :)
def google
end
end
Of course, you will need to setup routes for your action:
# config/routes.rb
post "login/:action", controller: 'login'
And add Koala (or whatever else you'll use to manage external APIs for which good Ruby packages already exist) to Gemfile:
# Gemfile
gem 'koala'
Then in your terminal run:
bundle install
rails server
And your app is up and running. Well, you'll need to setup your Facebook and Google applications first, get developer keys, and authorize localhost to accept auth_tokens.
Basically, that's it.