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.
Related
For over a year, I have connected to Salesforce using the simple_salesforce package in order to pull some data from various objects and load it to a data lake.
I have used the authentication method using username / password / security token.
client = Salesforce(
username="****************",
password="*************",
security_token="****************"
)
On the 1st of February came the enforcement of multi factor auth. Starting on that day, I consistently hit the same error over and over.
[{'message': 'This session is not valid for use with the REST API', 'errorCode': 'INVALID_SESSION_ID'}]
After some research, I tried to add a permission set with API Enabled and then API Only user. Result: still the same error, but now I am locked out of the UI.
Has anyone else encountered similar issues and could point me towards the right resources, please? Thanks!
MFA shouldn't matter for API access according to https://help.salesforce.com/s/articleView?id=000352937&type=1 (Ctrl+F "API"), it's probably something else your admin did.
Username, password+token sounds like you're use SOAP login method.
See if you can create a "connected app" in SF to use the OAuth2 login method, more natural for REST API. I wrote a bit about it in https://stackoverflow.com/a/62694002/313628. In the connected app you should be able to allow API access, even full if needed. No idea if Simple has natural place for the keys though, it's bit rubbish if you'll have to craft raw http requests yourself.
Simple's documentation also mentions using JWT to log in (and that requires connected app anyway), basically instead of username + pass you go username + certificate + the fact admin preauthorised this user... You'll be fine until certificate expires.
The text part of https://gist.github.com/booleangate/30d345ecf0617db0ea19c54c7a44d06f can help you with the connected app creation; sample code's probably not needed if you're going with Simple
Gist of the problem is, I'm not developing an SPA, I'm developing a mobile app, with a backend in Flask. FlaskSecurityToo has provided me with some great features, and I'm now trying to use their password reset functionality. Here's my gripe.
I want to have the email send a deeplink, which users on the mobile app will click and get sent to the password reset form on the app. There's no UI view for this. But FlaskSecurityToo has logic that requires the server is first hit to validate the token, then redirects them to whatever has REDIRECT_HOST set. Which works great when I set the REDIRECT_BEHAVIOR as spa
Is there a way to tell Flask "Hey, don't worry about the need to validate the token from the initially provided password reset email, let the UI/Mobile app make the call to determine that whenever they want" from the provided configuration? Thus, relaxing the constraint on the host name / details of the url for a password reset, as long as a token exists? Or is this abusing some of the principles of FlaskSecurity that I don't grasp yet?
My current plan is to let it open a mobile browser, and hopefully the redirect forces the app open? I have little experience with deeplinks, so I'm testing and probing things as I learn.
You are correct about current FS behavior - here is a suggestion (not clean but it would be interesting if it was all you need) - the POST /reset/ endpoint is stand-alone - you don't have to call GET first - the POST will ALSO verify the token is valid. So the issue becomes how to generate the link for the email that has what you want. FS currently doesn't allow to configure this (that could be an enhancement) - but in 4.0.0 you can easily replace the MailUtil class and have your implementation of send_mail look for template='reset_instructions'. Now - at this point the email has already been rendered - so you would have to parse the body and rewrite the url (keeping the token intact). Ugly but doable - is this sufficient? If so I can see a few simple improvements in FS to allow more flexibility around emails.
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 am trying to use simple_salesforce to query salesforce data with Python. I am using my username and password, which I am 100% sure is correct. I got the org ID from logging into Salesforce and looking at my company profile. It's only a 15-digit ID. I am specifically using an orgID to avoid using a security token as I don't know what it is. What am I doing wrong?
Code:
from simple_salesforce import Salesforce
sf = Salesforce(instance_url='https://na1.salesforce.com', session_id='')
sf = Salesforce(password='password', username='email', organizationId='15 digit org id')
Output:
File "C:\Python27\lib\site-packages\simple_salesforce\api.py", line 100, in __init__
proxies=self.proxies)
File "C:\Python27\lib\site-packages\simple_salesforce\login.py", line 124, in SalesforceLogin
code=except_code, message=except_msg))
simple_salesforce.login.SalesforceAuthenticationFailed: INVALID_LOGIN: Invalid username, password, security token; or user locked out.
I wrote most of simple-salesforce (although not the organizationId part, as I don't have an IP-whitelisted account to test against)
The standard/vanilla/regular/99% of users should use version is the simple username, password, security_token method.
So something like this
from simple_salesforce import Salesforce
sf = Salesforce(username='nick#nickcatalano.com', password='nickspassword', security_token='tokenemailedtonick')
By far the most confusing part is the security_token part (and was the part I got snagged with.) It turns out the Security Token is emailed to you after a successful password reset. So if you go into your salesforce account and reset your password, I believe you'll end up with an email with the subject salesforce.com security token confirmation which will contain a Security Token in the email. That's your security_token.
To be honest, the security_token kwarg is more a convenience than anything. In the normal email/password/token flow that most users rely on what is actually being sent is email as the login and {password}{security_token} as the password. I believe you could concat that yourself and just pass in a email and password kwarg if you want, but I figured forcing people to concat the password and token themselves would get go against the simple part of simple-salesforce
There is a way to log in with simple-salesforce with only a username and password. No security token required:
from simple_salesforce import Salesforce, SalesforceLogin
session_id, instance = SalesforceLogin(username='<user>', password='<pass>')
sf = Salesforce(instance=instance, session_id=session_id)
# Logged in! Now perform API actions, SOQL queries, etc.
sf.query_all('<soql>')
Explanation
All examples using simple-salesforce begin with a call to the Salesforce constructor to log in. This constructor accepts either an existing session ID, or authentication credentials to log in and make a new session. When logging in, it calls the lower-level SalesforceLogin function to do the real work, but interestingly SalesforceLogin does not enforce the same constraints on its arguments—it issues the correct SOAP call to log in with just a username and password, without requiring a token or organization ID.
Using this trick, we call SalesforceLogin directly, obtain the new session ID, then pass it directly into the Salesforce constructor. From that point on, we are able to make authenticated API requests.
Note
The version of simple-salesforce on PyPI (i.e. pip install simple-salesforce) is very outdated with the simple-salesforce GitHub repository. The latest version supports additional login parameters like domain for login with custom domains. To get the latest version, use
pip install --upgrade https://github.com/simple-salesforce/simple-salesforce/archive/master.zip
(Pip-installing from zip is faster than using git+ssh:// or git+https://, as noted in this answer.)
Edit
How will resetting my password show me what the token is?
It just will. If user has ever before requested the security token (which is sent to you via email - so you need to have access to the email address associated with your user) - every subsequent password reset will result with new token being generated and emailed to you. On top of that, once you're logged in to the system (to the web version, not via API) you will have an option to reset your token (and again, this will send you an email).
It's like you haven't read or tried anything we have written!
Looking for an answer drawing from credible and/or official sources.
https://help.salesforce.com/htviewhelpdoc?id=user_security_token.htm
https://help.salesforce.com/HTViewSolution?id=000004502
https://help.salesforce.com/HTViewSolution?id=000003783
And from the library's documentation:
https://github.com/neworganizing/simple-salesforce
To login using IP-whitelist Organization ID method, simply use your
Salesforce username, password and organizationId
This. If your IP address is whitelisted - you don't need the token. If it isn't - you NEED to generate the token. Period.
Original answer
I'm not familiar with that Python library but... Go to Salesforce -> Setup -> My personal infromation and check login history. if it contains stuff like "failed: security token required" then you're screwed and you will have to use the security token.
I'm not aware of any bypass that uses org id (I've connected via API from PHP, Java, C#... so I'd be very surprised if that Python library had some magical way to bypass it. You probably are used to passing a session id that assumes you're already authenticated and have a valid session.
Another option would be to check your IP and add it to trusted IP ranges (it's an option in the setup). It's useful when for example whole office has same static IP; less useful if you're working from home.
If that's also a no-go - you might want to look for libraries that use OAuth2 instead of regular SOAP API to authenticate.
Although this is kinda late, somebody searching for this very same issue may be helped as to what I did.
I struggled by adding the company ID as well, but the problem here is, unless you're a self-service user, the company ID can be blank.
sf = Salesforce(password='password', username='email', organizationId='')
As other users mentioned, make sure you're using IP-White listing or it will not work.
A security token is an automatically generated key that you must add to the end of your password in order to log into Salesforce from an untrusted network. For example, if your password is mypassword, and your security token is XXXXXXXXXX, then you must enter mypasswordXXXXXXXXXX to log in. Security tokens are required whether you log in via the API or a desktop client such as Connect for Outlook, Connect Offline, Connect for Office, Connect for Lotus Notes, or the Data Loader.
To reset your security token:
At the top of any Salesforce page, click the down arrow next to your name. From the menu under your name, select Setup or My Settings—whichever one appears.
From the left pane, select one of the following:
If you clicked Setup, select My Personal Information | Reset My Security Token.
If you clicked My Settings, select Personal | Reset My Security Token.
Click the Reset Security Token button. The new security token is sent via email to the email address on your Salesforce user record.
If you ip is whitelisted / trusted and you still get invalid login not using the token, You MUST include the security_token='' in the connection string for it to work.
sf = Salesforce(username='USERNAME', password='PASSWORD', security_token='')
A security token is required to login.
Whenever your password is reset, your security token is also reset.
If you do not have a token and cannot reset it.
Try changing your password.
Thanks.
I was able to test that this was working with my security token against a developer org with no issues. This was all done as a standard user with no administrator privileges. Using the OrgId just failed out.
By resetting my password I received a new security token.
username = login for your instance.
password = your password
The code below should get you logged in.
from simple_salesforce import Salesforce
sf = Salesforce(username='username',password='password', security_token='whatever came in reset password')
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