The client of my Google Cloud Endpoints API is an JavaScript (AngularJS) web application hosted on the same Google App Engine application as the Endpoints API itself. My users authenticate using webapp2 sessions (datastore). They don't necessarily have a Google account. I want to be able to do a request to the Endpoints API like /api/users/me which would return the user data of the user who is currently logged in.
First I thought I had to implement a OAuth2 provider for my App Engine application, and then let the AngularJS application request a OAuth2 access token from my own App Engine OAuth provider (instead of the OAuth provider of Google, like the built in authentication mechanism does).
However, this comment suggests not implementing my own OAuth2 provider but instead providing arbitrary parameters in my request (in a message field, or in a HTTP header) to the Endpoints API. I guess that parameter should be a user token (some encrypted value unique to the logged in user?). That value should then be passed to the browser. Isn't that insecure? I would like not to serve my AngularJS application on HTTPS if possible (to save costs).
Is this a good use case for OAuth2? Or is OAuth2 only for granting third party applications access to user data?
In case OAuth2 is not the way to go: how to pass a user token securily to the browser and prevent man-in-the-middle attacks? Should the user token expire after a certain amount of time?
I've just finished implementing exactly what you've described. Basically this method does the trick:
def get_current_session(request_state):
cookies = werkzeug.http.parse_cookie(request_state.headers.get('Cookie'))
sess_cookie = cookies.get('mc_session')
parts = sess_cookie.split('|')
if len(parts) != 3:
logging.error('Cookie does not have 3 parts')
return False
signature = hmac.new(COOKIE_SECRET_KEY, digestmod=hashlib.sha1)
signature.update('|'.join(parts))
sig_hex = signature.hexdigest()
if compare_hashes(sig_hex, parts[2]):
logging.error('Cookie signature mismatch!')
return False
cookie_data = webapp2_extras.json.b64decode(parts[0])
return sessions_ndb.Session.get_by_sid(cookie_data['_sid'])
And you'd call that from your API method using:
session = get_current_session(self.request_state)
You can find all the details at: https://blog.artooro.com/2014/08/21/share-sessions-between-google-cloud-endpoints-and-webapp2/
Related
I am using azure for hosting web app and web api.
Environment configuration :
web api : Developed using django deployed/hosted on linux vm
Web app : Developed using Angular2 deployed/hosted on App service
For authentication I am using OAUTH2 protocol.
App registration details for OAUTH2
Under single tenant: "my-tenant-id-121"
Registered web api and provided access_as_user permissions
here suppose app_id/client_id : "my-api-id-123"
APP uri : 'api://my-api-id-123'
scope : 'api://my-api-id-123/access_as_user'
client secret is generated but not using it.
Registered web app and provided basic details, redirect uri etc
here suppose webapp app_id/client_id : "my-webapp-id-123"
Under app registration, provided api access permissions for above registered api using API permissions
Authentication :
client(web app): Using ng2-adal library
resource (web api) : using python "jwt" library for access token validation
I have done all the configuration, after authentication I am getting id_token for web app and access_token for postman client.
Token Validation:
I am validating access token at api side which I am receiving through Authorisation header.
I have followed all the references available, through jwt.verify() my token getting validated. Here I am also validating audience, for id_token it is aud: client_app_id and when using postman I am specifying scope, in that case aud :"api://my-api-id-123"
Here comes the main part:
While following all process I never used registered web api app_id i.e "my-webapp-id-123" anywhere.
Then how come client app getting authenticated also access token getting validated.
Also I tried to remove linking between web app and web api from azure app registration and tried to authenticate. In both cases I have received token and validated at api side.
My questions are -
why we need to register Web api at app registration on azure as it is not getting used?
In my current scenario which part I am missing, my concern is if I remove linking (on azure, at client app under api permissions) between "client_app" and "api_app", access_token /id_token retrieved at client app should not get validated at web api.
Thanks in advance.
I am able to authenticate the react app with AAD and I am able to get the access token.
I have the following questions:
My backend is in python flask (WEB API's). How do I make sure that every request sent by react app is also authenticated with the same token?
Should I register a different application for the backend( python flask) or I can use the client ID of the same frontend application?
If I am passing the token in the header while calling every API request from the frontend, how backend will verify is the token is valid? Also, should it verify every API request?
I have seen multiple options like flask-azure-oauth library and some other libraries. For frontend I have tried ADAL and MSAL libraries.
In frontend make sure you append the accessToken in you're each HTTP request like, writing a common HTTP module and use it across the react app. And to make sure you're app is authenticated with same token you need to wrap react app with adal or MSAL or react-adal.
You have to use the same client id which used in react app in you're python backend in-order to verify the token you're sending in the API request.
You need to add before_request hook in flask and verify the accessToken you receive in the request. reference link
you can also check react-adal package for AAD authentication.
I am building a Web App with Python and I would like to authenticate users.
The pyrebase package seems to be outdated and it generates dependency errors so I cannot use it.
I know that there is a function from the firebase-admin API that works like this:
from firebase import auth
email = example#example.com
user = auth.get_user_by_email(email)
But what if this user has a password? I would like to check if the both the email and the password are provided correctly. Thanks in advance.
The Firebase Admin SDK does not have the concept of a current user, so there's no API to "sign in" a user based on their credentials.
Since you're building a web app, the usual flow is to use the Firebase JavaScript SDK in your client-side code to sign the user in. If needed you can then send the ID token from the client to your Python code on the server, and perform user-based operations there.
Firebase Admin SDK doesn’t provide an API to validate and/or authenticate a user by their password.
However, Firebase provides the Firebase Auth REST API for this purpose. To use the REST API, you need to obtain your Web API Key from the Firebase console.
To Locate the Web API Key
Navigate to Project Settings from Firebase console, then find Web API Key on the General tab. The Web Api key is auto generated whenever you add app to your firebase project. You can also go to an app settings to find apiKey from firebaseConfig
Implement Authentication
Suppose you want to implement user sign in
def sign_in_with_email_and_password(email, password, return_secure_token=True):
payload = json.dumps({"email":email, "password":password, "return_secure_token":return_secure_token})
FIREBASE_WEB_API_KEY = 'the web API key here'
rest_api_url = "https://identitytoolkit.googleapis.com/v1/accounts:signInWithPassword"
r = requests.post(rest_api_url,
params={"key": FIREBASE_WEB_API_KEY},
data=payload)
return r.json()
References
https://betterprogramming.pub/user-management-with-firebase-and-python-749a7a87b2b6
https://firebase.google.com/docs/projects/api-keys
https://firebase.google.com/docs/reference/rest/auth
I'm trying to implement OAuth2 server for a RESTfull API with a login option through social platforms (Github, Facebook, Instagram) using Python and Falcon web framework.
But I've struggled to understand how this thing should work.
My current understanding led me to the following scheme:
1.1. On the API side, I'm creating an endpoint /auth/login/github which basically will tell the mobile app to redirect the client to the Github.com authorization page - github.com/login/oauth/authorize
1.2. On the Github authorization page user will be presented with the following screen:
1.3. After pressing Authorize user will be taken to the page specified in the callback parameter (Github OAuth service configuration) with the newly granted temporary authorization code. In my case URL will look like: my.api.com/auth/callback/github?code=AUTH_CODE
2.1. After receiving a callback request, I'm parsing/extracting passed Authorization Code and query Github.com from the backend in order to redeem Authorization Code and get Access Token (sending POST request using my Client ID and Client Secret to github.com/login/oauth/access_token)
2.2. If everything was successful Github will reply to my POST request with the Access Token, which I can use to get user profile details (e.g. e-mail)
3.1. Now that I know that authorization through the Github was successful (because I got users' email) I can grant my own Access Token to that user, so he can query my API endpoints. I do this just by adding randomly generating OAuth2 Token and inserting it into my database, simultaneously returning same token to the user by redirecting him to the mobile app using deep links (e.g.: myapp://token).
3.2. Finally mobile app can query my API endpoints by adding the following header to each request Authorization: Bearer 0b79bab50daca910b000d4f1a2b675d604257e42
Does that make sense and is this the correct way of doing the social authorization for RESTfull API's?
I'm using Falcon as the web framework for this project and Authlib as the OAuth2 library.
Its one way for sure. And it looks alright.
I'm going to make it simpler, and maybe its a bit clear whats happening.
1.1 [Mobile APP] redirects user to github.com/oauth/authorize?client_id=CLIENT_ID with the client id you registered with github
1.2 [Mobile APP] user comes via a redirect to fancy.app/callback/github?code=AUTH_CODE (this is the callback url you configure on github)
1.2.1 [Mobile APP] call your API endpoint with the AUTH_CODE
1.3 [API] confirm with github the AUTH_CODE is valid.
Up to this point we have user authentication; the user isn't a random guy, is user xxx on github.com and you have the information you requested.
Now, if you need to authorise this user on your API, after 1.3:
1.3.1 [API] generate a token
1.3.2 [API] store the token in some persistent storage
1.3.3 [API] define some expiration time for the token (actually the AUTH_CODE from github should have some expiration, use that)
1.3.4 [API] return the token to the mobile APP
This token we've generated is what the Mobile APP will use to authenticate the user on the API; no further calls to github (until expiration at least).
1.1. On the API side, I'm creating an endpoint /auth/login/github which basically will tell the mobile app to redirect the client to the Github.com authorization page - github.com/login/oauth/authorize
Instead of hard coding /auth/login/github, how about making it a query parameter on your API so that you can quickly integrate separate OAuth2 providers (Google, Facebook, etc.)
Your endpoint URL would now look like /auth/login/?provider=github and your backend can provide the correct redirect url for the mobile app to go to. This means you can simply add new buttons for Facebook /auth/login/?provider=facebook and it would be minimal work.
When you receive the callback request, the URL may then look something like my.api.com/auth/callback/?provider=github&code=AUTH_CODE. You may also want to insert a new user record to your own database (if you have one), so you can prompt for extra info if required, I would do this in Django for example, since I require extra info on top of the data that is provided by third-party OAuth2 providers.
Overall, the approach looks sound.
I'm trying to use the Google Docs API with Python+Django and OAuth 2. I've got the OAuth access token, etc. via google-api-python-client, with the code essentially copied from http://code.google.com/p/google-api-python-client/source/browse/samples/django_sample/plus/views.py
Now, I assume I should be using the google gdata API, v 2.0.17. If so, I'm unable to find exactly how to authorize queries made using the gdata client. The docs at http://packages.python.org/gdata/docs/auth.html#upgrading-to-an-access-token (which appear outdated anyway), say to set the auth_token attribute on the client to an instance of gdata.oauth.OAuthToken. If that's the case, what parameters should I pass to OAuthToken?
In short, I'm looking for a brief example on how to authorize queries made using the gdata API, given an OAuth access token.
The OAuth 2.0 sequence is something like the following (given suitably defined application constants for your registered app).
Generate the request token.
token = gdata.gauth.OAuth2Token(client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
scope=" ".join(SCOPES),
user_agent=USER_AGENT)
Authorise the request token. For a simple command-line app, you can do something like:
print 'Visit the following URL in your browser to authorise this app:'
print str(token.generate_authorize_url(redirect_url=REDIRECT_URI))
print 'After agreeing to authorise the app, copy the verification code from the browser.'
access_code = raw_input('Please enter the verification code: ')
Get the access token.
token.get_access_token(access_code)
Create a gdata client.
client = gdata.docs.client.DocsClient(source=APP_NAME)
Authorize the client.
client = token.authorize(client)
You can save the access token for later use (and so avoid having to do the manual auth step until the token expires again) by doing:
f = open(tokenfile, 'w')
blob = gdata.gauth.token_to_blob(token)
f.write(blob)
f.close()
The next time you start, you can reuse the saved token by doing:
f = open(tokenfile, 'r')
blob = f.read()
f.close()
if blob:
token = gdata.gauth.token_from_blob(blob)
Then, the only change to the authentication sequence is that you pass this token to OAuth2Token by specifying a refresh_token argument:
token = gdata.gauth.OAuth2Token(client_id=CLIENT_ID,
client_secret=CLIENT_SECRET,
scope=" ".join(SCOPES),
user_agent=USER_AGENT,
refresh_token=token.refresh_token)
Hope this helps. It took a while to work it out :-).
This is from https://developers.google.com/gdata/docs/auth/overview:
Warning: Most newer Google APIs are not Google Data APIs. The Google Data APIs documentation applies only to the older APIs that are listed in the Google Data APIs directory. For information about a specific new API, see that API's documentation. For information about authorizing requests with a newer API, see Google Accounts Authentication and Authorization.
You should either use OAuth for both authorization and access or OAuth 2.0 for both.
For OAuth 2.0 API are now at https://developers.google.com/gdata/docs/directory.