How do I use two-legged oauth with the python gdata library? - google-oauth

I am trying to access a list of users through an app. I cannot get two-legged auth to work. I want to be able to access google's APIs without requiring user consent after install. The gdata lib was deprecated earlier this year, but a business requirement requires accessing the APIs without additional user consent after the domain admin has installed the app.
Here is what I have done so far:
I have created an application on the Marketplace. The app manifest declares this scope:
https://apps-apis.google.com/a/feeds/user/
I have installed my app in a test domain and granted permissions for the above scope.
I have looked at the samples here: https://code.google.com/p/gdata-python-client/source/browse/samples/oauth/
Deprecated Service approach: 2_legged_oauth.py
Updated Client approach: TwoLeggedOAuthExample.py
I am using Python 2.7.5 and gdata 2.0.18. Here is my code, which was adapted from the above mentioned examples:
import gdata.gauth
import gdata.apps.client
CONSUMER_KEY = "[snip].apps.googleusercontent.com"
CONSUMER_SECRET = "[snip]"
requestor_id = 'myuser#mydomain.com'
client = gdata.apps.client.AppsClient(domain='mydomain.com', source='myapp-v1')
client.auth_token = gdata.gauth.TwoLeggedOAuthHmacToken(
CONSUMER_KEY, CONSUMER_SECRET, requestor_id)
client.ssl = True
# Failure occurs here.
feed = client.RetrieveAllUsers()
The response:
<HTML>
<HEAD>
<TITLE>Unknown authorization header</TITLE>
</HEAD>
<BODY BGCOLOR="#FFFFFF" TEXT="#000000">
<H1>Unknown authorization header</H1>
<H2>Error 401</H2>
</BODY>
</HTML>
Here are the details for the headers being sent:
GET /a/feeds/mydomain.com/user/2.0?xoauth_requestor_id=myuser%40mydomain.com HTTP/1.1
Host: apps-apis.google.com
Accept-Encoding: identity
GData-Version: 2.0
Authorization: OAuth oauth_nonce="035378574830673", oauth_timestamp="1383593346", oauth_consumer_key="[snip].apps.googleusercontent.com", oauth_signature_method="HMAC-SHA1", oauth_version="1.0", oauth_signature="[snip]"
User-Agent: test-test-v1 gdata-py/2.0.18
Here is debugging information on the response (as taken from putting atom.http_core.ProxiedHttpClient into debug mode):
reply: 'HTTP/1.1 401 Unknown authorization header\r\n'
header: WWW-Authenticate: GoogleLogin realm="http://www.google.com/accounts/ClientLogin", service="apps"
header: Content-Type: text/html; charset=UTF-8
header: Date: Mon, 04 Nov 2013 19:33:35 GMT
header: Expires: Mon, 04 Nov 2013 19:33:35 GMT
header: Cache-Control: private, max-age=0
header: X-Content-Type-Options: nosniff
header: X-Frame-Options: SAMEORIGIN
header: X-XSS-Protection: 1; mode=block
header: Server: GSE
header: Alternate-Protocol: 443:quic
header: Transfer-Encoding: chunked
How do I use use two-legged oauth to access users on a domain? I am open to using the newer Google API clients (oauth2client and apiclient), but only if I can use two-legged authentication since I have a business requirement to not have the third leg.

I recommend you to read the docs regarding OAuth2 Service Accounts.
2-legged-OAuth is deprecated, gdata APIs are largely deprecated and apiclient services are much cleaner and easier to use.

Related

AWS Cognito google signin received x-cache: Error from cloudfront

I am getting 500 internal server error while trying to configure google sign in with my Cognito user pool, steps followed are from here.
There is no error message in response to the requested API.
I'm finding it very difficult to troubleshoot the issue. Only thing observed from response headers is
x-cache: Error from CloudFront
The error is from token endpoint URL: oAuth2/token
Request URL: https://mydomainurl/oauth2/token
Request Method : POST
Headers:
Content-Type: application/x-www-form-urlencoded
Form body:
grant_type: authorization_code
client_id: cognito_app_client_id_here
code: AUTH_CODE
redirect_uri: https://redirect-url
Received response headers:
access-control-allow-credentials: true
access-control-allow-origin: https://mydomainurl
cache-control: no-cache, no-store, max-age=0, must-revalidate
content-type: application/json;charset=UTF-8
date: Fri, 08 Nov 2019 08:10:28 GMT
expires: 0
pragma: no-cache
server: Server
status: 500
strict-transport-security: max-age=31536000 ; includeSubDomains
vary: Origin
via: 1.1 1111111222333.cloudfront.net (CloudFront)
x-amz-cf-id: DirdxkYdQojw7EE8fw-_Kta_nHfBIQBN9VpT2qTg3WGoj2a_8yh3RA==
x-amz-cf-pop: BLR50-C3
x-amz-cognito-request-id: 97468dc9-8957-4a35-8780-1a7f5d9e3c56
x-cache: Error from cloudfront
x-content-type-options: nosniff
x-frame-options: DENY
x-xss-protection: 1; mode=block
Does anyone know how to resolve the above error?
Where to view any error logs if generated by CloudFront distribution or by
CloudWatch for the above request?
I had a similar problem with a web app hosted on S3, distributed by CloudFront, with OAuth2 user authentication via Cognito.
In my case, the web app was a single-page application with routing. The app was hosted at https://example.com but my callback URL was at https://example.com/login. Since the login process involved navigating away from the app then redirecting back, it resulted in a 404 Not Found on the S3 side since there was no resource at s3://example.com/login, subsequently causing the x-cache: Error from cloudfront as seen in the response headers of the original question.
The fix was to edit the CloudFront distribution and add a custom error response, mapping 404 Not Found to a response path of /index.html and response code 200 Ok.

How to Validate an Access Token for OAuth2 + PCKE flow

According to this document
https://developers.onelogin.com/openid-connect/guides/auth-flow-pkce
Token Endpoint for PCKE flow is None (not Basic or POST)
So, how can I use the validation token API https://developers.onelogin.com/openid-connect/api/validate-session because it supports Basic authentication or POST but not for None (PCKE) I can't find any information relate to this.
NOTE: I have tried to request with Basic authentication and without + client_id, client_secret as a parameter but not working.
response 401 Unauthorized
{
"error": "invalid_client",
"error_description": "client authentication failed"
}
I'm using OIDC with PKCE, and I managed to call the https://openid-connect.onelogin.com/oidc/token/introspection endpoint with a token retrieved via the authorization code flow:
$ curl -i -d "token=...&token_type_hint=access_token&client_id=..." https://openid-connect.onelogin.com/oidc/token/introspection
HTTP/1.1 200 OK
Cache-Control: no-cache, no-store
Content-Length: 304
Content-Type: application/json; charset=utf-8
Date: Thu, 25 Apr 2019 23:37:42 GMT
Pragma: no-cache
X-Powered-By: Express
Set-Cookie: ol_oidc_canary_040819=false; path=/; domain=.onelogin.com
{"active":true,"sub":"...","client_id":"...","exp":1558819177,"iat":1556227177,"sid":"...","iss":"https://openid-connect.onelogin.com/oidc","jti":"...","scope":"openid profile email"}
Both the access_token and refresh_token returned from the auth code flow https://developers.onelogin.com/openid-connect/api/authorization-code-grant worked, and the access_token only returned {"active":false} after it expired.
Make sure you are not setting the Authorization header, and only set your client_id in the payload.
Use client_id and code_verify in the POST body. That will authenticate the request on that endpoint.

How to authenticate into a Django app using RemoteUserBackend

My app is required to support users logged in via SSO on a 3rd party server.
I configured settings.py based on the docs, i.e.
MIDDLEWARE_CLASSES = [
'...',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.auth.middleware.PersistentRemoteUserMiddleware',
'...',
]
AUTHENTICATION_BACKENDS = [
'django.contrib.auth.backends.RemoteUserBackend',
]
I tried to test this using Postman on a couple of the app's URLs with no auth and with basic auth (user is defined) and, of course, with REMOTE_USER (and/or HTTP_REMOTE_USER header set).
In all cases I get a 401 - unauthorized code. Moreover, the breakpoint in authenticate is never called. The process_request in the middleware is called, but the REMOTE_USER header is not in request.META.
What else do I need to configure (in Django, Postman - or better still Apache) so that the REMOTE_USER will be set?
My knowledge of Apache is minimal, so a link to an example will help a lot.
The closest "solution" I saw is this, but it seems that the person circumvented the proper way to do this.
UPDATE
The Postman request is simply to one of the basic services which requires users to be logged in (#login_required decorator in Django)
I've tried with both basic auth and no auth.
The reply is a 401 without additional information.
>curl -i -H 'REMOTE_USER: user' localhost:9000/project/files/
HTTP/1.0 401 Unauthorized
Date: Sun, 17 Dec 2017 13:38:38 GMT
Server: WSGIServer/0.1 Python/2.7.10
Expires: Sun, 17 Dec 2017 13:38:38 GMT
Vary: Cookie
Last-Modified: Sun, 17 Dec 2017 13:38:38 GMT
Location: /accounts/login/?next=/project/files/
Cache-Control: no-cache, no-store, must-revalidate, max-age=0
X-Frame-Options: SAMEORIGIN
Content-Type: text/html; charset=utf-8
Same command with cURL. The Location seems to suggest it tried to redirect to the login page (which should not happen)

Web API Basic Authentication returns 401

I've build a web api service with basic authentication and using a global DelegatingHandler implementation which I hook up to the web API GlobalConfiguration, in order to extract the username:password credentials from the request and hook an IPrincipal to the HttpContext if the credentials map to a valid user.
I've tested my api thoroughly on localhost and it's working fine, but not quite when hosted on IIS on a VPS.
I've hooked up remote debugging on the VPS in order to inspect whats going on and it turns out that whenever I include the authorization header to my request, the breakpoints I have set on the message handler are not getting hit, meaning that the request does not reach the handler. If I remove the Authorization header from the request, the breakpoint is getting hit and the handler is able to process it.
Since the message handlers are the first that will process the request in the pipeline (from what I know of, correct me if I'm wrong) I guess there must be an IIS or setup issue that I'm not aware of that messes the authentication process.
Fiddler Request Headers
GET http://myip/api/v1/route/parameter HTTP/1.1
Content-Type: application/json; charset=utf-8
Authorization: Basic ZHJpdmVyOjEwMTAyMDAz
Host: myip
Fiddler Response Headers
HTTP/1.1 401 Unauthorized
Cache-Control: no-cache
Pragma: no-cache
Content-Type: application/json; charset=utf-8
Expires: -1
Server: Microsoft-IIS/8.5
X-AspNet-Version: 4.0.30319
WWW-Authenticate: Basic realm="myip"
X-Powered-By: ASP.NET
Date: Mon, 22 Aug 2016 15:04:15 GMT
Content-Length: 61
{"Message":"Authorization has been denied for this request."}
What could be possibly be wrong, where should I look at for a solution?
EDIT
Had to disable Basic Authentication from the Authentication menu on the right pane setting for the IIS application.

Reddit API Oauth2 "user required"

I am trying to get the installed app to work with Oauth2 on Reddit's api. I am using Windows runtime api's httpclient to make requests, and webauthenticationbroker to get the code to do the GET request to receive the token. I requested a token by using this:
https://www.reddit.com/api/v1/authorize?client_id=" + client_id + "&response_type=code&state=" + "testing" + "&redirect_uri=http://abcd.com&duration=" + "permanent" + "&scope=" + "vote,identity"
And got a code back, so I used POST on this (with the content type being: application/x-www-form-urlencoded):
https://www.reddit.com/api/v1/access_token
with the body being this:
grant_type=https://oauth.reddit.com/grants/installed_client&\
device_id="+id + "&code=" + code
(code and id is the code received in the first step and id is a generated UUID)
Then I got something like this back:
{"access_token": "--5e65dP1dI_1vgLbqvi7zRB6cnU", "token_type": "bearer", "expires_in": 3600, "scope": "*"}
So I extracted the token and got this:
--5e65dP1dI_1vgLbqvi7zRB6cnU
Then I tried to do a GET request on https://oauth.reddit.com/api/v1/me with these headers:
{
User-Agent: (testUWP client by /u/bored_reddit_user)
Authorization: bearer --5e65dP1dI_1vgLbqvi7zRB6cnU
}
I got these headers back with status code 403 reason phrase forbidden:
{
Connection: keep-alive
Server: cloudflare-nginx
Strict-Transport-Security: max-age=15552000; includeSubDomains; preload
Transfer-Encoding: chunked
cache-control: max-age=0, must-revalidate
x-ua-compatible: IE=edge
CF-RAY: 23f5127a6a2911a1-SJC
Date: Tue, 03 Nov 2015 03:42:58 GMT
x-frame-options: SAMEORIGIN
access-control-allow-origin: *
X-Moose: majestic
x-reddit-tracking: https://pixel.redditmedia.com/pixel/of_destiny.png?v=BZoi0ikdGrSYn9U9xM6GWeYcRRb0W50fSQuGYb1Q8Oe7E5WVB6qTA4hRqlx9vDfpLOKzpE3Z5Wo%3D
x-content-type-options: nosniff
x-xss-protection: 1; mode=block
access-control-expose-headers: X-Reddit-Tracking, X-Moose
}{
Content-Type: application/json; charset=UTF-8
}
and this content:
{"explanation": "Please log in to do that.", "reason": "USER_REQUIRED"}
I am at a loss as to what I'm doing wrong, can anyone help me out?
This wiki page on the reddit github gives a pretty good overview of reddit's implementation of OAuth2 and the different grant_types and what circumstances they are appropriate for. This page has some more information. I'm suspecting that reddit doesn't want you storing the client secret in an app that you install on a user's device since you can't keep it secure, and a user could figure out your CLIENT_ID.
I'm glad my comment could point you in the right direction, if you could accept this answer, I'd appreciate the rep.