Getting access token for scope from azure-appservice using easyauth - python

I am creating a very small webapp in python/Flask, running under the Azure App Service. The app is protected with Azure AD authentication, which relies on easyauth. The app registration has two configured permissions/scopes: https://graph.microsoft.com/User.Read (to get info about the current user) and https://management.azure.com/user_impersonation (to run queries on the Azure management infrastructure).
Whenever I access the app, I have to login. That is good. I am provided with a number of headers, including X-Ms-Token-Aad-Access-Token which seems to contain an access token.
When I access Microsoft Graph (thus using the https://management.azure.com/user_impersonation scope), all is good (I took away some error handling, for space and clarity):
#app.route("/userinfo")
def userinfo():
token = request.headers.get("X-Ms-Token-Aad-Access-Token")
data = requests.get(
"https://graph.microsoft.com/oidc/userinfo",
headers={ 'Authorization': f"Bearer {token}" },
).json()
When I run pretty much identical code to get the list of web apps, it fails with a simple "AuthenticationFailed" code (also without error handling):
#app.route("/webapp")
def webapp():
token = request.headers.get("X-Ms-Token-Aad-Access-Token")
data = requests.get(
f"https://management.azure.com/subscriptions/{subscription_id}/providers/Microsoft.Web/sites?api-version=2019-08-01",
headers={ 'Authorization': f"Bearer {token}" },
).json()
I suspect that the token provided by the app service / easyauth does not support the https://management.azure.com/user_impersonation scope, even though that is specified in the web application setup. But I have no idea, really. It Does Not Work, though. I know that.
The obvious questions: Is this possible? Am I missing something obvious? Is there an easy solution?
(Using MSAL instead of the declarative mechanism seems to work, but I was hoping to use the platform services as much as possible)

Yes, you can.
To call the Azure REST API, you need to leverage the additionalLoginParams in the authsettings of your web app, navigate to the resource explorer -> find your web app -> add ["resource=https://management.azure.com"] to additionalLoginParams like below -> PUT.
After the configuration, when the user login the app, it will let you consent the permission https://management.azure.com/user_impersonation, after the consent, the X-Ms-Token-Aad-Access-Token will be able to call the Azure REST API i.e. https://management.azure.com, but it will not be able to call Microsoft Graph any more, because one access token can only for one API resource, to get the user info, you can hit https://webappname.azurewebsites.net/.auth/me directly, it will include the user info. Also, make sure your user account has the RBAC roles in your subscription e.g. Contributor, Owner, because the permission of the token comes from the user.
For more details, see this similar issue.

Related

python - how to access an API from azurewebsites

I am trying to access an API which is at azurewebsites.net. I am new to Azure platform and I don't know if for accessing this API through my code, would I need any additional configurations in the Azure platform? I tried the normal request method to get the API data in python but it throws an authentication error. Do I need to register my app in the Azure?
Here's what I am trying:
r = requests.get("url",
headers={
"Accept": "application/json"},
cookies={},
auth=('email', 'pass'),
)
Could someone please guide me through this? Thankyou.
Because I don't know what settings your azure website has done, so I can only give you a general solution.
Steps to try:
First of all, make sure that you can access your api site normally. You can test other interfaces to ensure that the api site is running normally.
When the website is running normally, check whether the aad setting is made on the portal.
If not, please use postman to test the interface to ensure that it can be accessed normally in postman, and then use the code to test by checking the url, header and other parameters.
😋If yes, please obtain Beartoken and add it when accessing the interface. I see that you are using email and pass, then you can use ropc flow get Beartoken. Then you can bring this verification when you visit each interface.
Related post:
Is there a way to improve the performance of MSAL-browser js login?

How to setup python social auth for web app and for mobile app?

We have
An existing Django backend with Python social auth for signing in with Google, providing web-based application and an API for the mobile app.
An iOS mobile app with GoogleSignIn pod.
Now we would like to allow mobile app users to sign in with Google inside the app, and then authenticate them on the backend, so that they can access their personal data via the app.
So my idea of the algorithm is:
App uses the GoogleSignIn and finally receives access_token.
App sends this access_token to the Backend.
Backend verifies this access_token, fetches/creates the user, returns some sessionid to the App.
App uses this sessionid for further requests.
The problem is with the third step: token verification. I found two ways of verifying:
1. Python social auth flow
As described in the docs:
token = request.GET.get('access_token')
user = request.backend.do_auth(token)
if user:
login(request, user)
return 'OK'
else:
return 'ERROR'
This would be a preferred flow, since it already has all the required steps and is working perfectly with the web app (like, accounts creation, defaults for newly created users, analytics collection, etc.).
But the problem is that the backend and the app use different CLIENT_IDs for the auth. This is due to the limitations in the Google Developers Console: when creating credentials, you need to select whether it will be a web app or an iOS app, and it cannot be both.
I tried to use different client ids (then backend cannot verify), tried to use web id inside the app (then the pod does not work), and tried to use app id inside the web (then the backend cannot verify again).
2. Google API Client Library
Another option is to utilize the way from the Google Sign-In for iOS documentation:
from google.oauth2 import id_token
from google.auth.transport import requests
try:
idinfo = id_token.verify_oauth2_token(token, requests.Request(), CLIENT_ID)
userid = idinfo['sub']
except ValueError:
# Invalid token
pass
It worked, but here we're missing all the pipeline provided by social auth (e.g. we need to create a user somehow), and I could not find a proper way of starting the pipeline from the middle, and I'm afraid it would be quite fragile and bug-prone code.
Another problem with this solution is that in reality we also have Signed in with Apple and Sign in with Facebook, and this solution will demand ad-hoc coding for each of these backends, which also bring more mess and unreliability.
3. Webview
Third option would be not to use SDKs in the Swift and just use a web view with the web application, as in the browser.
This solves the problem with the pipeline and client ids.
But it doesn't look native, and some users may suspect phishing attempts (how does it differ from a malicious app trying to steal Google identity by crafting the same-looking form?). Also, I'm not sure it will play nicely with the accounts configured on the device. And it also will require us to open a browser even for signing in with Apple, which looks somewhat awkward. And we're not sure such an app will pass the review.
But, maybe, these all are minor concerns?
⁂
So, what do you think? Is there a fourth option? Or maybe improvements to the options above? How is it solved in your app?

Where to store web authentication session in PySide?

I'm building a little application in Python. I use PySide for the GUI and Django to read data from my web application.
Everything works well, but I have a login access, like dropbox application.
I want to store this informations on the current machine (like a session, I don't want to login every time I open the application).
Now my question is, what is the safest way to do this? Environment variables?
Usually when you have an API that you're exposing in your app to the outer world (even your own desktop/mobile app), you'll design this API to be stateless, as part of the REST architecture. So your app should always include an HTTP header or any other method of carrying an authentication token that will let your API identify the user.
You only log in once, and when the log-in procedure is successful you should get an authentication token from your API, and then you will store this token somewhere safe.
You can also look into implementing OAuth2 for the authentication.

How can I call an endpoint in my appengine instance without doing oAuth? (Mirror API)

I am trying to create some Glassware with the Mirror API. I am new to using AppEngine and Jinja2. I have python experience but never with a web framework before. So basically I am very new at this.
I have modified the Python quickstart for the mirror API to include many of my endpoints and designs. Basically I want to be able to be able to POST data from a constrained device to Glass. I have an endpoint all setup which works to accept and parse out the data and send the timeline item.
My problem is that the device itself is acting all on it's own and cannot provide input, therefore when I call my app from it e.g. https://foo.appspot.com?operation=deviceData the app presents the auth page and then nothing happens. I can see in the logs that the auth page is being sent, but the device has no idea what to do with this.
Basically, I need a way where I can hardcode credentials and get around having to do oauth everytime. What is the recommended way to do this? Another app which doesn't require auth which passes the data along? This would be fine as I only need to set this up with one user right now, it is for an internal demo only.
Is it possible to set my credentials in a header and auth automatically without handling any return, more like how basic auth works?
There are also the "Simple API access" keys. Would these work in this situation, I tried creating browser and server keys and tried them on the device and in the browser by doinghttps://foo.appspot.com?operation=deviceData&key=KEY_HERE but in both cases I was still prompted to login. Is this what simple access keys are for? Do they not work with the mirror API?
Basically my question is, what's the easiest way to allow access to my apps endpoints without having to oAuth or having a hard coded user which auto-auths?
Here is the project that I started with: https://github.com/googleglass/mirror-quickstart-python

gdata-python-api + Analytics with simple auth

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')

Categories