My organization uses OpenAM SSO for authentication and my app is written in Pyramid.
The user id will be passed in HTTP header. I can also configure it to pass groups and permissions as well which I can use in acl. This makes the authentication in pyramid redundant. Is it possible to do away with Authenticaion Policy altogether and go with authorization alone?
You need a way to tell pyramid's authorization system who the person is (their effective principals). That is the responsibility of the authentication policy, even if it's something as simple as parsing a header.
class CustomAuthenticationPolicy(object):
def effective_principals(self, request):
principals = [Everyone]
identity = request.headers.get('x-identity')
# validate the identity somehow
if is_valid(identity):
principals += [Authenticated, identity, 'g:editors']
return principals
config.set_authentication_policy(CustomAuthenticationPolicy())
Related
I am running a django app with a REST API and I am protecting my API endpoint with a custom permission. It looks like this:
class MyPermission(permissions.BasePermission):
def has_permission(self, request, view):
host = request.META.get('HTTP_HOST', None)
return host == "myhost.de"
)
The idea is that my API is only accessible via "myhost.de".
Right now I am testing this with pytest. I can set my headers with:
#pytest.fixture()
def request_unauth(self, client):
result = client.get(
"myurl",
headers={'HTTP_HOST', 'myhost.de'},
content_type="application/json",
)
return result
def test_host(request_unauth):
assert request_unauth.status_code == 200
Since I can easily fake my headers I assume that this might also be easily done with other tools? How can MyPermission be evaluated from a security perspective?
Thanks so much for any help and hints. Very much appreciated.
Checking the Host header like that does not make sense, and will not protect against 3rd party clients as you described in comments. An attacker can create an arbitrary client and send request to your API, and that request can (and will) include the correct Host header as any other legitimate request.
Also based on your comments, you want to authenticate the client application, which is not technically possible, as it has been discussed many times. With some work (the amount of which you can influence somewhat) anybody can create a different client to your API, and there is no secure way you could prevent that, because anything you include in your client will be known to users (attackers), and will allow them to copy it. You can and probably should authenticate your users though, check access patterns, implement rate limiting, revoke user access in case of suspicious activity and so on - but this is all based on user authentication.
You can also prevent access from a client running in a standard browser on different domain, by sending the correct CORS headers (or not sending CORS headers at all) in your API.
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.
I have received the task of adding HTTP authentication(BasicAuthAuthenticationPolicy) to a Pyramid application that already have a AuthTktAuthenticationPolicy implemented...
Basically I need to create a RESTful API to authenticate users(Could I use BasicAuthAuthenticationPolicy for that?).
Is there a way to check whether a user is using the web interface, or using the api - to check which Authentication Policy to use?
I have not come across documentation that covers two different Authentication Policies in a single Pyramid application(if its even possible).
PS:
I have come across a blog series that started showing how to create a RESTful API with the pyramid framework... The Blogger reported that there was going to be 6 articles in the sersies, however I only managed to find two of those articles: Building a RESTful API with Pyramid - Setup and Building a RESTful API with Pyramid - Resource and Traversal. I am/was looking forward to his last article: Building a RESTful API with Pyramid - Authentication and ACL, but it doesn't seem like he is going to finish the series.
To recap my questions:
Could I use BasicAuthAuthenticationPolicy for building a RESTful api to authenticate users?
Is there a way to check whether a user is using the web interface, or using the API - to check which Authentication Policy to use?
Any help would be Appreciated.
So what I did was I merged Pyramids BasicAuthAuthenticationPolicy and CallbackAuthenticationPolicy and I ended up with this.
I have modified the callback method to use a redis session
To use this class(HTTPAthentication) you can do something like(this is an example how I implemented it for my usecase):
def _get_userid(details, request):
userre = re.compile("""^([a-zA-Z1-9\.]+):([a-zA-Z1-9\.:-]+)$""", re.I)
userpass = base64.b64decode(details[1])
res = userre.search(userpass)
if res:
return res.group()[0]
def authcheck(username, password, request):
user = Users.by_username(username, enabled=True)
host = request.registry.settings.get("authentication.host", "127.0.0.1")
maxattempts = request.registry.settings.get("authentication.maxattempts",5)
base = _get_userid(request.authorization, request)
if request.redis.exists("pimssess:%s" % base64.b64encode(request.remote_addr+":"+base)):
store = pickle.loads(request.redis.get("pimssess:%s" % base64.b64encode(request.remote_addr+":"+base)))
if store.get("auth.attempts").get(request.remote_addr):
if store["auth.attempts"][request.remote_addr] >= maxattempts:
raise HTTPMethodNotAllowed(body="You have been locked out for 5 minutes")
if user and user.agent and not user.is_http_auth:
raise HTTPMethodNotAllowed(body="You are not permitted http access")
if user and user.agent and user.host != host:
raise HTTPMethodNotAllowed(body="Your host is not permitted http access")
if user and user.agent and not user.validate_password(password):
time.sleep(1.5)
raise HTTPForbidden(body="Failed login, Incorrect password")
return getGroups(username)
The getGroups function retruns a list of groups that is attached to the user , i.e. ['admin', 'reporting']
I followed this example: BasicAuthAuthenticationPolicy(scroll to bottom)
And for the web interface login(CallbackAuthentication), you create a login interface, and create the view to accommodate the template(checking for password and username match, etc.)
Oh I almost forgot... In your project __init__.py, when you call the AuthPolicy, in the def main(...). I did:
authentication = AuthPolicy(secret='##^&*$!DSYUIDSA8321789DS',
hashalg='sha512', check=authcheck, debug=True)
authorization = ACLAuthorizationPolicy()
config = Configurator(settings=settings, root_factory=RootFactory,
authentication_policy=authentication,
authorization_policy=authorization)
I do hope this can help someone.
Pyramid does not make it easy to use different policies for different parts of the app (maybe something to work around with custom decorators) but for multiple policies check out pyramid_authstack. I'm using it with Session and BasicAuth policies for the same purpose as you are.
If it's not straightforward having one pyramid app with two auth policies you can have TWO separate Pyramid apps with a different policy each assembled into a single WSGI stack. Each app can import the same Python code, so, essentially, it'll be two startup files using the same views and everything.
If your apps have different URLs you can use paste.urlmap for this, and if your requirements are more complex you can even write your own router (say, requests with a certain HTTP header are routed to one app and without it to another)
I've looked around for this but can't seem to find a canonical answer. I'm trying to follow best practices. Maybe I'm thinking of it the wrong way.
I'm thinking of my API users as two different types: developers and end users, with separate django models in separate applications.
Developers will build clients for the API, and have access to certain resources of the API without the need of users login in. To limit their access, I would require them to register and in exchange give them an API key. We would also dogfood this to say, build a site frontend using Angular and iOS app.
Once those developers build their API clients, users of my site, which have already created a user account, will use the API clients created by developers. In the request from those clients I would expect a developer name, api_key as well as username/password (digest, if its our own trusted client and oauth token for thid party developers). This will require to check 1) developers are allowed to use the API by checking their APIKey, and 2) authenticate the end user. Is this possible in tastypie?
Am I going about this the wrong way? How would I do the double authentication?
We run a production site with this exact scheme. Of course you'll have to do your own tunning. But the general idea is good. You could have some OAuth inplace too, but we've found that it's not worth it. OAuth is too complicated for most cases.
I'll explain roughly what we do.
This is the App/developers part:
We identify "apps" (iOS, Android, BB, the site). Each app has an ApiClient instance model. The ApiClient has three attrs: name, public key, and private key.
We exchange the public and private keys through a safe channel with the ApiClient owner (the app).
The app must send every request indicating the public key and a signature generated with the private key (using hmac).
Everytime we get a request, we get the public key from the request, look it up in the DB, see what App it belongs too (the name) and check the signature. If everything is ok, the request is fulfilled.
For the user authentication part:
To authenticate a user we use other model ApiKey (provided by tastypie). Each user has an ApiKey. That model stores a unique (we could say random) string. When the user gets to the app he/she logs in into your API. The app should issue a request similar to this one:
POST /api/v1/login/
{
'username': 'XXX',
'password': 'XXX'
}
(please note that it always need to pass the previous public/private key auth)
If the user provided the right credentials we return an ApiKey unique key.
Every following request made by the app in behave of that user must include the key. That's the way you identify which user is trying to do each action.
An example of this last part:
User Jon logs in in the iOS app. (using regular username and password)
The app sends the request:
POST /api/v1/login/
{
'username': 'jon',
'password': 'snow'
}
We have a login API method. We check if the user exists and if the pass is ok. Suppose it's ok.
We sent the ApiKey info:
200 OK
{
'username': 'jon',
'key': '$123$'
}
The app has authenticated the user. It needs to use those credentials.
The user tries to do something in your app. Suppose he tries to get the datetime from your app. The app will issue this request:
GET /api/v1/date/
Authorization: ApiKey jon:$123$
That's it. It's not super safe. The ApiKeys are not invalidated. But that's because we create our own internal Apps. It's worth to note that we borrow some stuff from Tastypie from this. Check this out: http://django-tastypie.readthedocs.org/en/latest/authentication.html#apikeyauthentication
This is tangental, but you may want to check out drf-keypair-permissions.
It's a Django module that uses asymmetric public/private keypairs to sign and verify the HTTP request using pre-shared keys. It can pair each public key with a user so the authorization doubles as a login, and can be extended to manage API throttling.
It supports a few algorithms, including RSA-SHA and elliptic curve, and keys can be managed in the admin area.
It uses the IETC Cavage-12 draft standard for processing the Authorization signature
I'm setting up a Flask app with the flask-login extension. The flask-login documentation recommends setting up an alternative token generator that does not simply use the user ID and app secret to create the session token (which is the default method). But it doesn't provide any clear recommendations for how to do this.
So, for User.get_auth_token(), I'm using the make_secure_token function with the user email and password as parameters (so I get a hash of these parameters + app secret).
Next, I need to be able to get the user from the token with the token_loader callback. The default method for generating tokens in flask-login is to include both the raw user ID and a hash of the user ID + app secret. That makes finding the user from the token pretty simple - just grab the ID and look up the user.
But should I be exposing the user ID in the session token at all? If I don't, should I store the session token in the database or somewhere else with the user ID to make a lookup possible?
In short: does anyone know what the best practice is for creating a secure token & corresponding token_loader callback?
On the Flask mailing list, Matt Wright pointed me to his implementation in the flask-security extension. He uses itsdangerous to create a signed token which encodes a serialized (via URLSafeTimedSerializer()) list consisting of the user ID and the password hash. The token can then be decoded to grab the user ID.