I'm creating a web based service that I want to expose as a REST API so that developers are able to create apps using it. I want developers to be able to create/manage user accounts and authenticate through API. How to handle this? OAuth or something else?
I'm using python,flask,mongodb for this.
We have settled on the following, using OAuth 2 (which is much preferable to OAuth 1). In particular we are using the resource owner password credentials flow. As to how to integrate it into our RESTful service, here is the idea:
The initial resource, when hit by an unauthorized user, returns a 401. The body of the 401 contains a single link, with rel=oauth2-token. (How you signal links depends on your media type; we're using HAL, but you could use even just the Link header.)
After the user authenticates, he returns to the initial resource, sending in his Authorization header the bearer token returned from the OAuth 2 process. At this point, we return a 200, with all the normal links available.
We don't expose account creation, but if you wanted to do that, I would do so with another link available to unauthorized users in the initial resource. That link would have a custom rel since it is specific to your application, e.g. rel=http://rels.myapi.com/users
Good RESTful design would indicate that the link with this rel points to e.g. http://myapi.com/users, and that consumers of the API do a POST to that endpoint, which returns to them the new user resource with a Location header pointing to the newly-created user resource at e.g. http://myapi.com/users/username. (User resources themselves would of course be another rel, distinguishing between the singular user resource and the plural users collection resource.)
Related
I am trying to be sure that I understand it correctly:
Is OAuth a bridge for only third party authenticator those so common like Facebook, Google? And using it improves user experience in secure way but not adding extra secure layer to Django login framework? Or only Authorization Code grant type is like that? Can I take it like this?
What is OAuth?
According to RFC 6749:
The OAuth 2.0 authorization framework enables a third-party
application to obtain limited access to an HTTP service, either on
behalf of a resource owner by orchestrating an approval interaction
between the resource owner and the HTTP service, or by allowing the
third-party application to obtain access on its own behalf.
Essentially, it is an authorization protocol used to share permissions between multiple applications.
If you decide to implement OAuth, your application will be the one to allow other services to programmatically view your users' data and act on their behalf, if needed.
Whenever an application requires access to another service that you use, it probably uses OAuth to perform those actions. (e.g. When games used to ask us to allow posting on Facebook on our behalf.)
What OAuth is not?
By looking at your question, I feel like there's a misunderstanding of OAuth.
OAuth is not a bridge for third-party authentication methods. If you are looking for this type of authentication mechanism, you should take a look into Single Sign-On (SSO). For Django, you can use django-simple-sso.
Does it enhance security?
Depending on the use case, yes, it can enhance security.
If your application needs to exchange information with other services, it is a good practice to limit what these third-party services are able to do in your app, feature and time-wise.
Let's say, for example, that your user needs to give permission to another application to gather information from yours:
If you were to use the old-fashioned e-mail and password combination method, these credentials would be exposed in case of this third-party service had a data breach.
Using OAuth on the other hand is much more secure, as the credentials stored in the server would not contain the user's password and have very specific roles, apart from being easily revoked.
If you have a Django app I would say that you don't have to implement OAuth - you should be fine with any login functionality Django offers.
OAuth is commonly used when different services talk to each other. These don't have to be third-party services, they can belong to the same party. For example, when you have a Single Page Application or a Mobile App that want to call your backend API to get some data. Then it's better to use OAuth as it is a standard and it helps you to implement authorization in a secure way.
When you think about "login with Google/Facebook", what you actually want is an SSO solution (Single Sign-On). That solution is very often implemented with OpenID Connect (OIDC), which is a protocol built on top of OAuth. Still, you can use just OIDC to log a user in with Google, get an ID Token, and then be able to authenticate the user to your Django app based on the ID Token from Google. You don't need OAuth for that (in the sense, that you don't need to get access tokens from Google, you don't need your own Authorization Server, and you can rely on cookie-based sessions).
I'm attempting to make SOQL queries to the Salesforce API using the Python salesforce_api and simple-salesforce modules. I had been making these requests with a client object:
client = Salesforce(username='MY_USERNAME',
password='MY_PASSWORD',
security_token='MY_SALESFORCE_SECURITY_TOKEN')
a = client.query("SELECT something FROM some_object_table WHERE some_condition")
However, my company recently restricted Salesforce sign-in through SSO only (you used to be able to login directly to Salesforce without SSO), and the funciton is throwing either:
simple_salesforce.exceptions.SalesforceAuthenticationFailed: INVALID_SSO_GATEWAY_URL: the single sign on gateway url for the org is invalid
Or:
salesforce_api.exceptions.AuthenticationMissingTokenError: Missing or invalid security-token provided.
depending on which module I use. I suspect this is because of the SSO implementation.
I've seen the docs about creating a new app through Okta, but I need to authenticate and access the API of an existing app. What is the best way to access this API with Okta IdP enabled? It there a way to have a get request to Okta return an access token for Salesforce?
Uh. It's doable but it's an art. I'll try to write it up but you should have a look at "Identity and Access Management" Salesforce certification, study guides etc. Try also asking at salesforce.stackexchange.com, might get better answers and Okta specialists.
I don't know if there's pure server-side access to Okta where you'd provide OAuth2 client, secret, username and password and it'd be silently passed to login.
If your app is a proper web application that needs human to operate - you can still make it work with SSO. You'd have to read about OAuth2 in general (you saw it on the web, all the "login with Google/Facebook/LinkedIn/Twitter/..." buttons) and then implement something like this or this. Human starts in your app, gets redirected to SF to enter username and password (you don't see password and you don't care whether he encountered normal SF login page or some SSO), on success he/she is redirected back and you receive info that'll let you obtain session id (sometimes called access token). Once you have access token you can make queries etc, it's just a matter of passing it as HTPP Authorization Bearer header (simple-salesforce docs mention session id at top of the examples).
Look, I know what I've written doesn't make much sense. Download Data Loader and try to use it. You might have to make it use custom domain on login but there is a way for it to still work, even though you have SSO enforced. Your goal would be to build similar app to how Data Loader does it. This might help a bit: https://stackoverflow.com/a/61820476/313628
If you need a true backend integration without human involved... tricky. That might be a management problem though. They should not enforce SSO on everybody. When Okta's down you're locked out of the org, no way to disable SSO. You should have a backup plan, some service account(s) that don't have SSO enforced. They might have crazy password requirements, maybe login only from office IP address, whatever. It's not a good idea to enforce SSO on everybody.
https://help.salesforce.com/articleView?id=sso_tips.htm
We recommend that you don’t enable SSO for Salesforce admins. If your
Salesforce admins are SSO users and your SSO server has an outage,
they have no way to log in to Salesforce. Make sure that Salesforce
admins can log in to Salesforce so that they can disable SSO if
problems occur.
(If you have a web app and it's embedded as Canvas in SF - there's another clean way to have the session id passed to you. Again - this works only if you have a human rather than backend integration)
If you check the profiles in SFDC and uncheck the box that requires SSO.
"is single sign-on Enabled [] Delegate username and password authentication to a corporate database instead of the salesforce.com user database. "
Since Google Auth does not support wildcards in its redirect URL's, I am trying to create a proxy solution for Google Authentication.
I have read the following two posts but need some more clarifications
How to deal with arbitrary amount of redirect URIs?
Subdomain in Google Console Redirect URIs
Primarily I have one domain say www.abcxyz.com
I need to have the Google Auth mechanism work for number of subdomains say:
alpla.abcxyz.com
beta.abcxyz.com
and so on for dynamically created subdomains
Following is something I want to try:
I will have only 1 redirect URL in the client secret json file say google.abcxyz.com
When creating a redirect url I pass the name of the subdomain 'alpha' in the state parameter
Once auth code is sent from google to google.abcxyz.com my proxy code reads the state parameter and forwards the request to alpha.abcxyz.com. This domain will then exchange the auth code for access token and refresh token.
Will this kind of mechanism work or do I need to do something more?
Will access tokens work from the subdomain?
What happens when access token expires?
I initially thought that this issue will be a big hindrance in automating my system but looks like there is an easy fix for this.
The redirect url should contain the primary domain and should be present both in Google client secrets and Google API console.
Pass the subdomain name in the state.
In the Primary Domain Apache Config read the query string and redirect accordingly.(read this blog to learn how to do redirect's based on query string).
I'm working on converting a Python script using the Google gdata API client + user/pass authentication to something more suitable for production (an API key). I am pretty frustrated with the muddled state of their documentation on authentication. I admittedly don't have a great grasp of OAuth2, but it seems like it's way more complicated for my usage case, which is: Hit Google Analytics every 24 hours to get the X most popular articles on our site.
In this scenario, we're not dealing with modifying someone's personal data, and all activity is centered on one account. It doesn't seem like OAuth2 is worth the complexity for something so simple.
I see that on the Google API Console (https://code.google.com/apis/console/), I've registered there and notice that there's a "Simple API Access" section with one key beneath the "Client ID for web applications" (which appears to be OAuth2). There's also the Google domain update page, https://www.google.com/accounts/UpdateDomain, but that appears to be OAuth related.
Is there any way to use this Simple API Access key (not OAuth) for retrieving analytics data with the Python gdata client, and if so, does anyone have any authentication examples? I already have the data retrieval stuff working once authenticated, but I'm using the user/pass approach, which is not appropriate for production.
Greg,
If you are already using the library gdata-python-client, this is relatively easy to do if you are the only user that your application will be authorizing.
The general mechanisms were detailed in a blog post in September, 2011, but I'll describe them here for completeness.
Part 1: Go to the APIs console and start a new project.
Part 2: From the project, go to "Services" and enable "Analytics API"
Part 3: From the project, go to "API Access" and click "Create an OAuth 2.0 client ID..." (you'll need to provide a product name, though the value you provide won't matter). When asked for the application type, select "Installed Application" and then "Create client ID". Since you will be the only user, you will only need one refresh token, and you can get this by authorizing from a desktop application a single time.
Part 4: Get your client id and client secret from the APIs console and then create an empty token:
import gdata.gauth
CLIENT_ID = 'id-from-apis-console'
CLIENT_SECRET = 'secret-from-apis-console'
SCOPE = 'https://www.google.com/analytics/feeds/' # Default scope for analytics
token = gdata.gauth.OAuth2Token(
client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
scope=SCOPE,
user_agent='application-name-goes-here')
I got the scope from GData FAQ, though I'm not sure if it is correct.
Part 5: Use the token to create authorization URL for you to visit:
url = token.generate_authorize_url(redirect_uri='urn:ietf:wg:oauth:2.0:oob')
Since your application is an "Installed Application", your redirect URI is the default 'urn:ietf:wg:oauth:2.0:oob'. (Also note, the blog post had a typo and used the keyword argument redirect_url.)
Part 6: Visit the url and authorize your application to make requests on behalf of your account. After authorizing, you'll be redirected to a page with a code on it. This code will be used to exchange for an access token and a long-lived refresh token. The code has a life of 10 minutes and the access token has a life of an hour. The refresh token will allow you to get new access tokens for signing requests in perpetuity (or until you revoke the permission from your account).
Part 7: Use the code to get an access token:
code = 'random-string-from-redirected-page'
token.get_access_token(code) # This returns the token, but also changes the state
This again differs slightly from the blog post, because we are using an installed application.
Part 8: With the token you can now make all requests you want to make to the analytics client:
import gdata.analytics.client
client = gdata.analytics.client.AnalyticsClient()
token.authorize(client)
This is the big money right here. When an access token expires, the API requests signed with that token are rejected. However, by authorizing the client as above, when the said requests fail, the token attempts to use the refresh token to obtain a new access token. If it successfully obtains a new access token, the client resends the original API request, signed with the new access token.
I don't know anything about the Analytics API so I won't provide any more details there.
Future Use Note 1: Saving information for future use. You can re-use this from different places and after this use very easily. There are methods called token_to_blob and token_from_blob provided by the library that allow turning a token into a string and converting out of a string:
saved_blob_string = gdata.gauth.token_to_blob(token)
Once you have done this, you can store the string in a file and kill your running Python process. When you'd like to use it again:
saved_blob_string = retrieve_string_from_file() # You'll need to implement this
token = gdata.gauth.token_from_blob(saved_blob_string)
Future Use Note 2: This token will be able to be used to authorize a client and perform all your magic again and again, so long as you have the refresh token around. If for some reason you would like to get an access token again without calling token.generate_authorize_url, you'll need to manually set this on the object:
token.redirect_uri = 'urn:ietf:wg:oauth:2.0:oob'
Future Use Note 3: Also, if you lose your refresh token and would like to get another one without having to go to the browser to revoke the original, you can use the approval_prompt parameter to get a new refresh token by visiting the url generated by:
url = token.generate_authorize_url(
redirect_uri='urn:ietf:wg:oauth:2.0:oob',
approval_prompt='force')
I have a google app engine project and i want to make my REST API available to third parties. Like twitter API.
Twitter requests the user and password to be sent so thet they can be authorized - can i do this with google user accounts?
I have read somewhere on this site this is not a prefered method as their credentials are entered into a thirdparty app/passed etc.
I dont want to over complicate and redirect them to a googles own login/captcha and so on. I have seen a implementation of this - also with desktop/phone/widgets this may not even work.
Is a solution for a third party apps to call my service - the app calls a page on my site (which asks for login - i just add login rights) the user then logs in and my app then creates a long token that is then passed back to third party app which then stores this in their own repository and pass it as a Token arg in the post/put/delete request.
My REST service then checks the token and against a lookup (token|user key) pairs and allows/rejects the method call.
I could also ask the app for their domain? and store this too. I guess i can read the url of the request on the REST service and check they match etc?
Is this a reasonable solution or am i living in the 90's?
OAuth was created with this situation in mind. It's a specification for the sort of auth-token scheme you just described, and it's reasonably widespread these days.
For balance, see this recent StackOverflow thread.