OAuth: Receive callback with parameters as POST data - python

I'm new to OAuth, I'm using the oauth2 library on Python to get my work done.
Currently when I receive a callback from the server, the parameters come in the URL as:
http://mydomain/?oauth_verifier=(SOME_DATA)&oauth_token=(SOME_DATA)&oauth_callback_confirmed=true
I'm wondering if it's possible to instruct the server to somehow POST those parameters (oauth_verifier, oauth_token, oauth_callback_confirmed) to me as a callback and not show them in the URL (as a GET request)?
Thank you!

No, it is not possible to encode the callback parameters as a POST request. The OAuth 1.0 Spec says that the provider issues an HTTP Redirect to the callback URL:
If the Consumer provided a callback URL in oauth_callback (as
described in Consumer Directs the User to the Service Provider), the
Service Provider constructs an HTTP GET request URL, and redirects the
User’s web browser to that URL with the following parameters:
Since an HTTP Redirect can only be a GET, not a POST, your callback can only contain the parameters in the URL.

Related

How can i redirect to an external page from fastapi's exception handler

I'm writing a web-app that has to support Single-Sign-On through Saml. The back-end is made with FastAPI and the front end is made in react. Nginx is used to proxy requests to the backend. I have an endpoint for logins that returns a redirect request to an identity provider. When accessing the endpoint directly through the browser everything works as expected and the user is redirected to an external login page.
I want to be able to use a similar redirect when a user who isn't authorized tries to access the page. My attempt at this consists of returning a redirect response, when a http exception is raised, to my login endpoint which should then redirect to the external login page. Doing this prompts a 404 response from the idp.
The login endpoint is defined like this in FastAPI:
#app.get("/saml/login")
async def sso(request: Request):
req = await prepare_from_fastapi_request(request)
auth = Saml2_Auth_patched(req, saml_settings)
callback_url = auth.login(return_to="https://website.com/")
response = RedirectResponse(url=callback_url)
return response
And the exception handler like this:
#app.exception_handler(StarletteHTTPException)
async def unauthorized_handler(request: Request, exc):
response = RedirectResponse(url="/api/saml/login")
return response
This looks more like a front-end issue since as you are saying everything works as expected when you access the endpoint in the browser.
I think the issue here is that the library you are using to make API requests from your front end application does not follow redirect. For example axios does not do that out of the box. You can see the conversation here: https://github.com/axios/axios/issues/1350
In the same conversation they mention another dependency you can use to address this issue: https://www.npmjs.com/package/follow-redirects

FastAPI's RedirectResponse doesn't work as expected in Swagger UI

I have a FastAPI app with a download endpoint. What this download endpoint does is to use a BlobServiceClient (for Azure Blob Storage) to generate a token and a Blob URL to a file specified in the request. What I want to do is to redirect the user to that URL. Here is a code snippet of the download enpoint (I commented some things out because I'm not allowed to show the code).
#router.get("..path", tags=["some tags"], summary=..., responses={404: {"model": ...}, 403: {"model": ...}, 307: {"model": ...}}, response_model_exclude_none=True)
async def download_file(
# there's a depends on an API key
blob_path: str = Query(
...
)):
credential = ClientSecretCredential(...) //secrets
blob_service_client = BlobServiceClient(f"https://{storage_account}.blob.core.windows.net", credential=credential)
user_delegation_key = blob_service_client.get_user_delegation_key(key_start_time=datetime.utcnow(),key_expiry_time=datetime.utcnow() + timedelta(minutes=30))
token = generate_blob_sas(account_name=...,
container_name=...,
blob_name=blob_path,
user_delegation_key=user_delegation_key,
permission=BlobSasPermissions(read=True),
expiry=datetime.utcnow() + timedelta(minutes=30))
blob_url = f'https://{storage_account}.blob.core.windows.net/{container_name}/{blob_path}?{token}'
print(blob_url)
response = RedirectResponse(blob_url)
return response
What I expected is the query to be executed, and after the response is returned, the download to start in the background or in a separate tab. What I've got instead is a different response as you can see in the Swagger:
I also had a look in the Network tab to see what is happening with that request:
Looks like there is an OPTIONS request and I assume that I'm getting the response to that request. Not sure if this is how Swagger handles the request. Any idea how/why this is happening and how to fix it? Thank you!
To start with, the HTTP OPTIONS, in CORS, is a preflight request that is automatically issued by the browser, before the actual request—is not the one that returns the File response. It requests the permitted communication options for a given server, and the server responds with an Access-Control-Allow-Methods header including a set of permitted methods (e.g., Access-Control-Allow-Methods: OPTIONS, GET, HEAD, POST, DELETE). The preflight response can be optionally cached for the requests created in the same URL using Access-Control-Max-Age header, thus allowing the server to limit the number of preflight requests. The value of this header is expressed in seconds; hence, allowing caching for 10 minutes, for example, would look as Access-Control-Max-Age: 600.
As for the RedirectResponse, Swagger UI always follows redirect responses. In a fetch request, for instance, the redirect parameter would be set to follow, indicating that the redirect should be followed. This means that Swagger UI follows the redirect and waits for the response to be completely received before providing you with a Download file link (as shown in the screenshot you provided above) that would allow you to download the file. That is also why you can't see the download starting either in the background or in a new tab. As mentioned in the linked github post above, it is not possible to change that behaviour, which could allow you to handle it differently, similar to the approach demonstrated in this answer.
Instead of using Swagger UI to test that specific endpoint, you can either test it directly through typing the URL to your API endpoint in the address bar of your browser (since it is a GET endpoint, you can do that, as when you type a URL in the address bar of your browser, it performs a GET request), or create your own custom Template (or use HTMLResponse) and submit an HTML <form>, as shown in this answer.

How to get oauth2-token for Google Analytics Reporting API (REST method) in Python

I want to route my Google Analytics Reporting API request (code will be in AWS Lambda) through a gateway which accepts a REST endpoint only. Since I cant use the Client package method in my interaction with the gateway, I need to query the API as a REST-ful endpoint.
The official document says this (Link) :
Authorization: Bearer {oauth2-token}
GET https://www.googleapis.com/analytics/v3/data/ga
?ids=ga:12345
&start-date=2008-10-01
&end-date=2008-10-31
&metrics=ga:sessions,ga:bounces
I do not know to create the oauth2-token in Python. I have created a service account and have the secrets_json which includes the client id and secret key.
Then client package method as given in this link works. But I need the Rest method only!
Using these, how can I create the oauth2-token ?
You can use Oauth2 for this I have done it in the past but you will need to monitor it. You will need to authorize this code once and save the refresh token. Refresh tokens are long lived they normally dont expire but your code should be able to contact you if it does so that you can authorize it again. If you save the refresh token you can use the last step at any time to request a new access token.
Oauth2 is basicly built up into three calls. I can give you the HTTP calls i will let you work out the Python Google 3 Legged OAuth2 Flow
Authencation and authorization
The first thing you need is the permission of the user. To get that you build a link on the authorization server. This is a HTTP get request you can place it in a normal browser window to test it.
GET https://accounts.google.com/o/oauth2/auth?client_id={clientid}&redirect_uri=urn:ietf:wg:oauth:2.0:oob&scope=https://www.googleapis.com/auth/analytics.readonly&response_type=code
Note on redirect uri. If you are running this on a server or something then use urn:ietf:wg:oauth:2.0:oob it basicly tells the server to return the code back where it came from other wise if you are hosing on a website you can supply a url to the page that will be handling the response.
If the user accepts the above then you will have an authorization code.
Exchange code
What you need to do next is exchange the authorization code returned by the above response and request an access token and a refresh token. THis is a http post call
POST https://accounts.google.com/o/oauth2/token
code=4/X9lG6uWd8-MMJPElWggHZRzyFKtp.QubAT_P-GEwePvB8fYmgkJzntDnaiAI&client_id={ClientId}&client_secret={ClientSecret}&redirect_uri=urn:ietf:wg:oauth:2.0:oob&grant_type=authorization_code
The body parameter should be as i have shown separated by & and the content type of the request is application/x-www-form-urlencoded
Responce
{
"access_token" : "ya29.1.AADtN_VSBMC2Ga2lhxsTKjVQ_ROco8VbD6h01aj4PcKHLm6qvHbNtn-_BIzXMw",
"token_type" : "Bearer",
"expires_in" : 3600,
"refresh_token" : "1/J-3zPA8XR1o_cXebV9sDKn_f5MTqaFhKFxH-3PUPiJ4"
}
The access token can be used in all of your requests to the api by adding either an authorization header bearer token with the access token or by sending access_token= as your parameter in your requests.
Refresh access token
Refresh tokens are long lived they should not expire they can so you code should be able to handle that but normally they are good forever. Access tokens are only valid for one hour and you will need to request a new access token.
POST https://accounts.google.com/o/oauth2/token
client_id={ClientId}&client_secret={ClientSecret}&refresh_token=1/ffYmfI0sjR54Ft9oupubLzrJhD1hZS5tWQcyAvNECCA&grant_type=refresh_token
response
{
"access_token" : "ya29.1.AADtN_XK16As2ZHlScqOxGtntIlevNcasMSPwGiE3pe5ANZfrmJTcsI3ZtAjv4sDrPDRnQ",
"token_type" : "Bearer",
"expires_in" : 3600
}

Use JWT with TurboGears2

I'm currently stopped in my work because of some authentication work on a project.
I set up a REST API, which needs to have a JWT authentication system.
Some work was already done and I overrode it. So the library used was Python's TurboGears2, and I used PyJWT to manage tokens.
My WS and the token's creation works well. The post method with auth info JSON request's body can create a token, that's sent in the response.
But after that, when I do a 'GET' request on the restricted resource, I can't retrieve the token.
What I do: send a GET request to the restricted resource, with "Authorization: Bearer <TOKEN>" in request headers.
But when I do a 'request.authorization' in my web service function, I always get 'None'.
Do I need to set up a full auth system using TurboGears to access this header?
thanks for help
Where are you trying to access the request.authorization from?
I tried with a newly quickstarted application and modified the index to print the authorization header:
#expose('testauth.templates.index')
def index(self):
"""Handle the front-page."""
print(request.authorization)
return dict(page='index')
And I sent the authorization header from Postman.
It worked fine and printed my test header
Authorization(authtype='Bearer', params='HELLO')
I also tried to disable any auth_backend so that authentication is disabled and it still works as expected.

Oauth 1.0a in Django

I'm trying to make authorised calls on the Rdio API in my Django application. I've been looking at the following tutorial so far to get it set up:
http://www.rdio.com/developers/docs/web-service/oauth/ref-oauth1-overview
The code at the bottom of the page works fine for me: I can get the request token, authorise the user using the PIN, and then make a call using the new access token.
However, I'd like to implement the callback so that the user can just log in and return to my site so that I can make authorised requests with their account. I currently have a page with a link to authorise the application, where the function to get the link is like so:
def get_auth_url():
client = oauth.Client(consumer)
response, content = client.request('http://api.rdio.com/oauth/request_token', 'POST', urllib.urlencode({'oauth_callback': 'http://localhost:8080/my_page/'}))
parsed_content = dict(cgi.parse_qsl(content))
request_token = oauth.Token(parsed_content['oauth_token'], parsed_content['oauth_token_secret'])
sURL = '%s?oauth_token=%s' % (parsed_content['login_url'], parsed_content['oauth_token'])
return sURL
This is okay, and when I click this link I go to a page asking to authorise my account for this application. However, I then need to get the access token from the request token that my user has just authorised. The callback from the authorisation page gives me oauth_verifier and oauth_token arguments but constructing the request token requires oauth_token and oauth_token_secret. I had the secret on the first call but can't get it again in this second call, and the tutorial said that I shouldn't store the secret anywhere accessible or transfer it across requests. And since these are two different requests I can't think of where to store the persistent request token. How can I get the oauth_token_secret on this second request so that I can get the access token?
You'll need to store the request token on your server temporarily so you can make the access token request. This line:
The request token secret must be included in the signature but not over the wire.
refers to the fact that the secret is used to generate the signature, but isn't included by itself in the request.
To save yourself some time and effort, I recommend using Django Social Auth. It already supports Rdio.

Categories

Resources