I am trying to implement token based authentication for my Flask REST API. I am using Stormpath as my third-party authentication service.
I looked into flask-stormpath built on top of flask-login. Looks like it uses password based authentication as they are trying to maintain session on the server. Also, the documentation doesn't provide me enough information.
Do we have a flask integration for stormpath token based authentication ?
If yes, can someone point me to a sample code.
I have already gone through the stormpath/flask-stormpath-sample on github, which again maintains sessions in server.
References:
https://stormpath.com,
https://github.com/stormpath/stormpath-flask
So here is the way I am currently using until rdegges shall build this feature into flask-stormpath.
You will need stormpath python sdk latest version and wraps from func tools.
from stormpath.api_auth import (PasswordGrantAuthenticator, RefreshGrantAuthenticator, JwtAuthenticator)
from functools import wraps
You can create your application as such.
stormpathClient = Client(id=KEYS['STORMPATH_ID'], secret=KEYS['STORMPATH_SECRET'])
stormpathApp = stormpathClient.applications.search('your-application')[0]
This decorator shall help you with securing endpoints.
def tokenRequired(func):
"""
Decorator to apply on all routes which require tokens.
"""
#wraps(func)
def wrappingFunc():
#check the auth header of the request for a bearer token.
authHeader = request.headers.get('Authentication')
#make sure that the string is a bearer type.
if len(authHeader)<8 or (not authHeader[:7] == 'Bearer ') or (
not authHeader):
return Response("401 Unauthorized",401)
authToken = authHeader[7:]
try:
authenticator = JwtAuthenticator(stormpathApp)
authResult = authenticator.authenticate(authToken)
request.vUser = authResult.account
except:
return Response("403 Forbidden",403)
return func()
return wrappingFunc
#Use this decorator like below.
#flaskApp.route('/secure-route',methods=['GET','POST'])
#tokenRequired
def secureEndpoint():
# return JSON based response
return Response("This is secure Mr." + request.vUser.given_name ,200)
Let me know in the comments if someone wishes to know the token issuing and refreshing end points as well.
I'm the author of the Flask-Stormpath library. The answer is no. I'm actually working on a new release of the library (coming out in a month or so) that will provide this functionality by default, but right now it only supports session based authentication.
Related
I have an MSAL app that creates authentication tokens for accessing various Microsoft APIs.
I provide the app specific scopes, and it creates a corresponding authentication token bearing those scopes. This app works perfectly fine for all types of endpoint I tried up
def _create_or_get_msal_app_client(
self, client_id: str, tenant_id: str | None = None, is_confidential_client: bool = False
) -> msal.ClientApplication:
"""
Create public or confidential msal app client to generate tokens
:param client_id: the client id (also known as application id)
:param tenant_id: the tenant id to use as authority, if not provided will use common authority
:return: the msal app
"""
if self._msal_app:
return self._msal_app
try:
authority = tenant_id if tenant_id else "common"
authority_url = f"https://login.microsoftonline.com/{authority}"
if is_confidential_client:
self._msal_app = msal.ConfidentialClientApplication(
client_id=[client_id], client_credential=[client_credential], authority=authority_url
)
else:
self._msal_app = msal.PublicClientApplication(client_id=client_id, authority=authority_url)
return self._msal_app
msal_app = self._create_or_get_msal_app_client(
client_id=[client_id], tenant_id=[tenant_id]
)
return msal_app.acquire_token_by_username_password(
username=[username], password=[password], scopes=[some scopes]
)
The tokens produced if inputted into jwt.io, will be marked as invalid, which is not a bad thing in itself, as noted by this qustion.
My problem is, when I try to call APIs with endpoints of type:
https://admin.powerplatform.microsoft.com/api/*
It almost seems like those kinds of endpoints has a different authorization system than the rest of the endpoints; For once, the token this EP uses in the UI I tool it from have a perfectly valid signature when trying to decode it in JTW.io, as opposed to the token issues by MSAL. But, this means that now I get in the response a 401 response when I try to use the MSAL-issues tokens, and the reason for the failed request, is, according to the response header resp.headers._store['www-authenticate'][1] is:
Bearer error="invalid_token", error_description="The signature is invalid"
This doesn't happen in any other Microsoft API I tried to call; for example in EPs of type https://graph.microsoft.com/v1.0/* the token produced by MSAL works perfectly fine.
The prime suspect in these types of authentication errors is the scopes asked. But no matter what scopes I ask, whether I ask for insufficient or sufficient or no scopes at all, I still get the same error.
Except what was suggested here to try to ask for the scope [client_id]/.defualt (where client id is the client id) but when I try to do that I get the error:
Bearer error="invalid_token", error_description="The audience \'[client_id]\' is invalid"
in the response headers.
I have another clue about what might be the problem in this forum, where the one asking the question mentioned that the EP is using OAuth. could it be that this is different from MS Graph in any way?
So my question is, how do I configure my MSAL app to work with https://admin.powerplatform.microsoft.com/api/*? Or alternatively, what EP could I use instead that does work with MSAL, and contains the same functionality as this one?
Note: looking at the headers in the request to get the tokens in the UI, I see they are using msal.js.browser, so this should be possible in theory. (by the way, the requested scope in the UI is [client_id]/.defualt openid profile offline_access) to the EP https://login.microsoftonline.com/common/oauth2/v2.0/token). When trying to decode the UI token in jwt.ms it says that the token is issued by AAD.
Example of a concrete EP I am trying to access: https://admin.powerplatform.microsoft.com/api/Environments/{env_name}/roleassignments/environmentadmin. The API is taken from the Power Platform Admin Center. More info about it here.
I'm trying to create a sign-in card so that the person interacting with the card is redirected to a sign-in page and the token is then used for other purposes (like fetching information etc).
class SignInGeneratorService:
def __init__(self, APP_ID, APP_PASSWORD):
SETTINGS = BotFrameworkAdapterSettings(APP_ID, APP_PASSWORD)
self.ADAPTER = BotFrameworkAdapter(SETTINGS)
activity = Activity()
self.turn_context = TurnContext(self.ADAPTER, activity)
async def generate_raw_link(self):
return await self.ADAPTER.get_oauth_sign_in_link(self.turn_context, "ADAuth")
This is what I initially tried to get the sign in link, but it doesn't seem to work (probably because we are using a dummy turn context object). Any ideas on how to do the same without having to use dialogs?
Unless I'm misunderstanding your question, I think you can achieve this using the "signin" card. The 06.using-cards sample demonstrates how to set up the card. And, visiting the BotFramework-WebChat repo, you can test this using the 01.getting-started/a.full-bundle demo. Just type in the word "signin" and the card will be presented. Clicking the button brings you to a page to log into Microsoft's live.com site.
You would only need to update the URL so it points to the service of your choice or design allowing you to acquire whatever information necessary.
Hope of help!
Sharing what worked for me. I had to pass MicrosoftAppCredentials and set the bot identity using the bearer token I had created earlier using the Microsoft login API (needs the client id and secret for generating this). After this I was able to create a sign-in URL which could be passed on for further use.
identity = await self.ADAPTER._authenticate_request(self.activity,
"Bearer *bearerToken*")
self.app_credentials = MicrosoftAppCredentials(APP_ID, APP_PASSWORD)
self.turn_context.turn_state[BotAdapter.BOT_IDENTITY_KEY] = identity
sign_in_resource = await self.ADAPTER.get_oauth_sign_in_link(self.turn_context, "ADAuth", None, self.app_credentials)
I created 2 applications in my Azure directory, 1 for my API Server and one for my API client. I am using the Python ADAL Library and can successfully obtain a token using the following code:
tenant_id = "abc123-abc123-abc123"
context = adal.AuthenticationContext('https://login.microsoftonline.com/' + tenant_id)
token = context.acquire_token_with_username_password(
'https://myapiserver.azurewebsites.net/',
'myuser',
'mypassword',
'my_apiclient_client_id'
)
I then try to send a request to my API app using the following method but keep getting 'unauthorized':
at = token['accessToken']
id_token = "Bearer {0}".format(at)
response = requests.get('https://myapiserver.azurewebsites.net/', headers={"Authorization": id_token})
I am able to successfully login using myuser/mypass from the loginurl. I have also given the client app access to the server app in Azure AD.
Although the question was posted a long time ago, I'll try to provide an answer. I stumbled across the question because we had the exact same problem here. We could successfully obtain a token with the adal library but then we were not able to access the resource I obtained the token for.
To make things worse, we sat up a simple console app in .Net, used the exact same parameters, and it was working. We could also copy the token obtained through the .Net app and use it in our Python request and it worked (this one is kind of obvious, but made us confident that the problem was not related to how I assemble the request).
The source of the problem was in the end in the oauth2_client of the adal python package. When I compared the actual HTTP requests sent by the .Net and the python app, a subtle difference was that the python app sent a POST request explicitly asking for api-version=1.0.
POST https://login.microsoftonline.com/common//oauth2/token?api-version=1.0
Once I changed the following line in oauth2_client.py in the adal library, I could access my resource.
Changed
return urlparse('{}?{}'.format(self._token_endpoint, urlencode(parameters)))
in the method _create_token_url, to
return urlparse(self._token_endpoint)
We are working on a pull request to patch the library in github.
For the current release of Azure Python SDK, it support authentication with a service principal. It does not support authentication using an ADAL library yet. Maybe it will in future releases.
See https://azure-sdk-for-python.readthedocs.io/en/latest/resourcemanagement.html#authentication for details.
See also Azure Active Directory Authentication Libraries for the platforms ADAL is available on.
#Derek,
Could you set your Issue URL on Azure Portal? If I set the wrong Issue URL, I could get the same error with you. It seems that your code is right.
Base on my experience, you need add your application into Azure AD and get a client ID.(I am sure you have done this.) And then you can get the tenant ID and input into Issue URL textbox on Azure portal.
NOTE:
On old portal(manage.windowsazure.com),in the bottom command bar, click View Endpoints, and then copy the Federation Metadata Document URL and download that document or navigate to it in a browser.
Within the root EntityDescriptor element, there should be an entityID attribute of the form https://sts.windows.net/ followed by a GUID specific to your tenant (called a "tenant ID"). Copy this value - it will serve as your Issuer URL. You will configure your application to use this later.
My demo is as following:
import adal
import requests
TenantURL='https://login.microsoftonline.com/*******'
context = adal.AuthenticationContext(TenantURL)
RESOURCE = 'http://wi****.azurewebsites.net'
ClientID='****'
ClientSect='7****'
token_response = context.acquire_token_with_client_credentials(
RESOURCE,
ClientID,
ClientSect
)
access_token = token_response.get('accessToken')
print(access_token)
id_token = "Bearer {0}".format(access_token)
response = requests.get(RESOURCE, headers={"Authorization": id_token})
print(response)
Please try to modified it. Any updates, please let me know.
there is plenty of docs on how signed_request-s are build up, but could not find (and come up with) a simple method that creates a signed request
does anyone has a solution?
the basic docs are here:
I would need it for unit tests. More precisely, I'm using facebook-sdk, and just wrote a nice middleware for using facebook authentication together with tastypie. To test this I need the mock the auth process of facebook, and the last missing step is to create the cookies set in the browser when the auth happens.
The result will be open sourced.
Like following:
from hashlib import md5
def fb_signature(request, app_secret):
fb_request = dict([(k,request[k]) for k in request if k.startswith('fb_sig')])
payload = ''.join(['%s=%s' % (k[len('fb_sig_'):], fb_request[k]) \
for k in sorted(fb_request.keys()) if k != 'fb_sig' ])
return md5(payload + app_secret).hexdigest()
Here request --- your request to calculate signature, app_secret --- your FB app secret.
this is my first web-programming experience so I hope my questions doesn't sound very dumb. I have been stucked on this for many days.
I am trying to understand a sample code:
https://github.com/facebook/runwithfriends
However I am not understanding very well how the information flow works and how can I modify that sample (i.e. how the code works).
For example, in the following section of the code:
class RecentRunsHandler(BaseHandler):
"""Show recent runs for the user and friends"""
def get(self):
if self.user:
friends = {}
for friend in select_random(
User.get_by_key_name(self.user.friends), 30):
friends[friend.user_id] = friend
self.render(u'runs',
friends=friends,
user_recent_runs=Run.find_by_user_ids(
[self.user.user_id], limit=5),
friends_runs=Run.find_by_user_ids(friends.keys()),
)
else:
self.render(u'welcome')
As I understand (along with HTML) is useful for showing friends that are using the same app, and if I understand correctly, here is the essential part:
*friends_runs=Run.find_by_user_ids(friends.keys())*
But what if I want to show any given friend. How can I do it?
Summarizing, I would like to know:
1- How the flow of the code works? (I don't fully understand the explanation here)
2- How can I manipulate the code so to get, for example, to show a list of friends of the user (not necessary that use the same app)?
Moreover, Can I show friends filtered by some characteristic (for example, gender)?
Thanks a lot!
The python "SDK" for facebook I use I took from https://gist.github.com/1190267
and combined it with the code from the example app to achieve the functionality I wanted both for a canvas app and for website usage.
It depends whether you're using facebook with websites or a canvas application. For a canvas application you probably could do well with the javascript SDK but for a "login with facebook" I required serverside logic that should work with javascript turned off so I've completed that solution with details you might have help to know. You can try make small changes of that specific app 'runwithfriends' to get an understanding which code does what. The project you're looking at contains some outdated practice though:
getting and setting cookies is likely preferable now doing with webapp2's builtin functions for this instead of the code that comes with the FB example app
logging in and out is now done with OAuth 2.0 so it's likely that the login system you're looking at is outdated and you need to use OAuth 2.0 which is described here. I much rather do login/logout serverside so I did an OAuth 2.0 pure python solution to login / logout following the authentication steps mentioned in the tutorial from FB. I had to clear the cookie to log a user out which was not documented.
To upgrade to python 2.7 I had to also modify so that HTTP header did not cast to unicode. I don't know why but otherwise it complained that headers were "not strings"
To more elaborately answer your specific questions:
1) The requesthandler class you posted is a subclass of a BaseHandler so to fully understand what it does you can look at the BaseHandler class since what you are posting is a BAseHandler. The BaseHandler uses django templates for rendering and if you want to can switch the template engine to jinja2 which is remmended. Further the code accesses the user object inherited from the BaseHandler and does some operations on it and renders it to a template. You can try make a requesthandler of your own, subclass BaseHandler and do what you want.
2) I could manipulate the code and I'm not an expert so you should be able to do it too. I wanted a simple FB app to display random images and I could manipulate it to select random images via blobs and render to to a template while keeping the facebook base functions. A function to use for getting the user using the Graph API I do this:
def parse_signed_request(signed_request, secret):
"""
Parse signed_request given by Facebook (usually via POST),
decrypt with app secret.
Arguments:
signed_request -- Facebook's signed request given through POST
secret -- Application's app_secret required to decrpyt signed_request
"""
if '.' in signed_request:
(esig, payload) = signed_request.split('.')
else:
return {}
sig = urlsafe_b64decode(str(esig))
data = _parse_json(urlsafe_b64decode(str(payload)))
if not isinstance(data, dict):
raise SignedRequestError('Pyload is not a json string!')
return {}
if data['algorithm'].upper() == 'HMAC-SHA256':
if hmac.new(secret, payload, hashlib.sha256).digest() == sig:
return data
else:
raise SignedRequestError('Not HMAC-SHA256 encrypted!')
return {}
def get_user_from_cookie(cookies, app_id, app_secret):
"""Parses the cookie set by the official Facebook JavaScript SDK.
cookies should be a dictionary-like object mapping cookie names to
cookie values.
If the user is logged in via Facebook, we return a dictionary with the
keys "uid" and "access_token". The former is the user's Facebook ID,
and the latter can be used to make authenticated requests to the Graph API.
If the user is not logged in, we return None.
Download the official Facebook JavaScript SDK at
http://github.com/facebook/connect-js/. Read more about Facebook
authentication at http://developers.facebook.com/docs/authentication/.
"""
cookie = cookies.get('fbsr_' + app_id, '')
if not cookie:
return None
response = parse_signed_request(cookie, app_secret)
if not response:
return None
args = dict(code=response['code'], client_id=app_id,
client_secret=app_secret, redirect_uri='')
file = \
urllib.urlopen('https://graph.facebook.com/oauth/access_token?'
+ urllib.urlencode(args))
try:
token_response = file.read()
finally:
file.close()
access_token = cgi.parse_qs(token_response)['access_token'][-1]
logging.debug('returning cookie')
return dict(uid=response['user_id'], access_token=access_token)
See http://developers.facebook.com/docs/api for complete documentation for the API. And you can get the the official Facebook JavaScript SDK at http://github.com/facebook/connect-js/
I'm now writing code to sync a webapp2_extras.auth account with facebook so that custom accounts and facebook accounts can co-exist and we're discussing solutions for this in the webapp2 groups and categories. The current way I do it is adding the recommended current_user to a basehandler and using that as the FB identity while working on "merging" my class FBUser that is a custom class for facebook users that autheorized my website and/or canvas application to sync with webapp2_extras.auth.models.User which is an expando model so it can just add the properties it doesn't have such as facebookid, firstname, lastname, etc.
#property
def current_user(self):
if not hasattr(self, '_current_user'):
self._current_user = None
cookie = get_user_from_cookie(self.request.cookies,
facebookconf.FACEBOOK_APP_ID,
facebookconf.FACEBOOK_APP_SECRET)
if cookie:
# Store a local instance of the user data so we don't need
# a round-trip to Facebook on every request
user = FBUser.get_by_key_name(cookie['uid'])
if not user:
graph = GraphAPI(cookie['access_token'])
profile = graph.get_object('me')
user = FBUser(key_name=str(profile['id']),
id=str(profile['id']),
name=profile['name'],
profile_url=profile['link'],
access_token=cookie['access_token'])
user.put()
elif user.access_token != cookie['access_token']:
user.access_token = cookie['access_token']
user.put()
self._current_user = user
return self._current_user
You can also solve your authentication with session objects and build your authentication system around that. That is what I do when using both custom accounts and facebook accounts and you're welcome to have a lok at my repository for more code examples how to intregrate facebook with google app engine using python 2.7.