I'm trying to follow this Oauth2 guide for Sign in With Twitter https://github.com/simplegeo/python-oauth2 - Everything is going great until between steps 2 and 3. I handle the the callback fine, but how do I pass along the oauth_token_secret? My confusion is that it seems like it's lost after the redirect back to my handler.
From what I can tell the parameters I get back are oauth_token and oauth_verifier, and yet I need the oauth_token_secret to receive the access token in these steps.
token = oauth.Token(request_token['oauth_token'],
request_token['oauth_token_secret'])
token.set_verifier(oauth_verifier)
client = oauth.Client(consumer, token)
resp, content = client.request(access_token_url, "POST")
access_token = dict(urlparse.parse_qsl(content))
Am I supposed to store it in a cookie to retrieve later?
I was able to do this by storing the oauth_token and oauth_token_secret in a session during step one. These values are stored from the created request token request_token['oauth_token']
Related
I'm trying to test out Anti Forgery tokens with API calls using VS2019 (C#) and RestSharp. What i'm doing is doing a GET to our login page to get 4 tokens/ cookies, then attaching these to a POST, with the Username and password, to try to log on. The first call succeeds and gives me a HTTP 200 and 4 cookies/ tokens back (ASP.NET_SessionId, XSRF-TOKEN, XSRF-COOKIE and a __RequestVerificationToken - all get attached as cookies (in the cookiecontainer) to the POST API call), the 2nd call however, fails with an HTTP 500 with this message: "Validation of the provided anti-forgery token failed. The cookie "__RequestVerificationToken" and the form field "__RequestVerificationToken" were swapped.". I'm including this token twice in my POST call - once as a cookie and once as part of the request body. Here is my code - can anyone shed any light on how to fix this error?
Thanks,
Jamie.
public void LogIn(string userName, string password)
{
// 1st call to get the cookies and tokens.
CommonProperties.Client = new RestClient { CookieContainer = new CookieContainer() };
CommonProperties.Client.BaseUrl = new Uri($"https://localhost:50000/Account/Login");
var request = new RestRequest(Method.GET);
request.AddParameter("ReturnUrl", "%2F", ParameterType.QueryString);
CommonProperties.Response = CommonProperties.Client.Execute(request);
CommonProperties.Client.BaseUrl = new Uri($"https://localhost:50000/Account/Login");
var requestToken = CommonProperties.Response.Cookies.Single(c => c.Name ==
"__RequestVerificationToken");
// 2nd call to log in.
request = new RestRequest(Method.POST);
request.AddHeader("Content-Type", "application/x-www-form-urlencoded");
request.AddHeader("Accept-Encoding", "gzip, deflate, br");
request.AddHeader("Accept", "*/*");
request.AddHeader("Referer", $"https://localhost:50000/Account/Login");
request.AddParameter("undefined", $"__RequestVerificationToken=
{requestToken.Value}&UserName=userName&Password=password_321", ParameterType.RequestBody);
CommonProperties.Response = CommonProperties.Client.Execute(request);
}
I just solved this issue myself, so hopefully this answer helps anyone else who comes looking.
When you originally load the page with the GET it will return the cookie with the name of __RequestVerificationToken PLUS a form field with the same name. That form field will have a different value to the cookie's value, and in your POST you must set the body field for __RequestVerificationToken to have that form field's value, and the value of the cookie with the same name must match the cookie from the GET response.
If you have the same value for both the form field and the cookie, you get that rather misleading error that suggests the values are "swapped". They are not actually swapped, they're just matching, which is incorrect.
I have a SPA app that contains an form with an upload file field. I have a rest API whose endpoints are protected via flask-extended-jwt JWT. To authenticate the REST endpoints I use #jwt_required. I want to authenticate the upload request as well.
Because of the client side I can't add an Authorization Bearer header so I thought to add the access token as a hidden field when submitting the form.
What is the best way to manually validate the JWT access token after I read it from the form?
class Upload(Resource):
def post(self):
#TODO: check for access token
access_token = None
if 'access_token' in request.form and request.form['access_token']:
access_token = request.form['access_token']
else:
message = json.dumps({'message': 'Invalid or missing token', 'success': False})
return Response(response=message, status=401, mimetype='text/plain')
if access_token:
#TODO: validate_token(access_token)
Thank you
Author of flask-jwt-extended here. That's a great question. There is currently no supported way to do that in the extension, the grabbing the token from the request and decoding it are tightly coupled together. This would be hard to de-couple because there is a lot of conditional things that are going on when the full decode chain runs. For example, checking the CSRF value only if the request is sent in via a cookie, or differentiating between an access and refresh token for the sake of the blacklisting feature.
A generalized function could be created, it's signature would look something like decode_and_verify_jwt(encoded_token, is_access_token=True, check_csrf=False). However, this would complicate the rest of the code in flask_jwt_extended and be a rather confusing function to use for the general case.
I think in this case it would be easier just to add a fourth lookup in the extension, so you could use something like:
app.config['JWT_TOKEN_LOCATION'] = ['headers', 'forms']
app.config['JWT_FORM_KEY'] = 'access_token'
# Use the rest of the application normally
If you want to make a ticket on the github page so I can track this, I would be happy to work on it.
I getting back into Python and wanted to use the pyfoursquare package to access the Foursquare API. I'm trying to get information about venues using the venues method in the API class. I'm primarily trying to find out whether a venue page is verified with Foursquare or not. When I provide my client id, client secret, and venue id I keep getting back an error that states "Authentication required", which doesn't makes sense because I'm providing that information. Any help would be great. Thank you.
import pyfoursquare as foursquare
client_id = ""
client_secret = ""
callback = ""
auth = foursquare.OAuthHandler(client_id, client_secret, callback)
api = foursquare.API(auth)
result = api.venues("4e011a3e62843b639cfa9449")
print result[0].name
Let me know if you would like to see the error message. Thanks again.
I believe you are skipping the step of grabbing your OAuth2 access token, so you're not technically authenticated.
Have a look at the following instructions, under "How to Use It":
https://github.com/marcelcaraciolo/foursquare
The lines that might be useful to you are:
#First Redirect the user who wish to authenticate to.
#It will be create the authorization url for your app
auth_url = auth.get_authorization_url()
print 'Please authorize: ' + auth_url
#If the user accepts, it will be redirected back
#to your registered REDIRECT_URI.
#It will give you a code as
#https://YOUR_REGISTERED_REDIRECT_URI/?code=CODE
code = raw_input('The code: ').strip()
#Now your server will make a request for
#the access token. You can save this
#for future access for your app for this user
access_token = auth.get_access_token(code)
print 'Your access token is ' + access_token
Can I restrict actions of my API to specific users if I generate a token like this:
from itsdangerous import TimedJSONWebSignatureSerializer as Serializer
expiration = 600
s = Serializer(current_app.config['SECRET_KEY'], expires_in = expiration)
return s.dumps({ 'id': kwargs.get('user_id') })
And the verification
#staticmethod
def verify_auth_token(token):
s = Serializer(app.config['SECRET_KEY'])
try:
data = s.loads(token)
except SignatureExpired:
return None # valid token, but expired
except BadSignature:
return None # invalid token
user = User.query.get(data['id'])
return user
I don't understand how this works and achieves security. The way I'm used to securing an API for example, a user wants to do HTTP PUT to /posts/10 I'd usually get the post's author ie user_id then query the database get the token for that user_id, if the request token matches the queried token then it is safe for the PUT. I've read this article and don't fully understand how it achieves security without storing anything in a database. Could someone explain how it works?
By signing and sending the original token upon login the server basically gives the front end an all access ticket to the data the user would have access to, and the front end uses that token (golden ticket) on all future requests for as long as the token is not expired (tokens can be made to have expiration or not). The server in turn knows the token has not been tampered with, because the signature is basically the encrypted hash of the users recognizable data (user_id, username, etc). So, if you change the token information from something like:
{"user_id": 1}
to something like:
{"user_id": 2}
then the signature would be different and the server immediately knows this token is invalid.
This provides an authentication method that exempts the server from having to have a session, because it validates the token every time.
Here is an example of what a token could look like (itsdangerous can use this format of JSON web tokens)
I'm working on building a library for a client to integrate with LinkedIn's API and am currently working on the OAuth implementation. I am able to request the initial token's no problem and have the user grant the authentication to my app, but when I try to request the access token with the oauth_token and oauth_verifier (along with the rest of the oauth information that I send with ever request, I get a signature invalid error.
The OAuth settings that I send are as follows:
oauth_consumer_key
oauth_nonce
oauth_timestamp
oauth_signature_method
oauth_version
oauth_token
oauth_verifier
I also add in the oauth_signature which is a signed version of all of those keys. I sign the request with the following method:
public void function signRequest(any req){
var params = Arguments.req.getAllParameters();
var secret = "#Variables.encoder.parameterEncodedFormat(getConsumer().getConsumerSecret())#&#Variables.encoder.parameterEncodedFormat(Arguments.req.getOAuthSecret())#";
var base = '';
params = Variables.encoder.encodedParameter(params, true, true);
secret = toBinary(toBase64(secret));
local.mac = createObject('java', 'javax.crypto.Mac').getInstance('HmacSHA1');
local.key = createObject('java', 'javax.crypto.spec.SecretKeySpec').init(secret, local.mac.getAlgorithm());
base = "#Arguments.req.getMethod()#&";
base = base & Variables.encoder.parameterEncodedFormat(Arguments.req.getRequestUrl());
base = "#base#&#Variables.encoder.parameterEncodedFormat(params)#";
local.mac.init(local.key);
local.mac.update(JavaCast('string', base).getBytes());
Arguments.req.addParameter('oauth_signature', toString(toBase64(mac.doFinal())), true);
}
I know that it works, because I can use the same method to request the initial token (without the oauth_token or oauth_verifier parameters), so I am assuming that it is a problem with the parameters that I am signing. (And yes I am alphabetically ordering the parameters before I sign them)
So is there a parameter that I shouldn't be including in the signature or another one that I should be?
This is an example of a base string that gets encrypted:
POST&https%3A%2F%2Fwww.linkedin.com%2Fuas%2Foauth%2FaccessToken&oauth_consumer_key%3Dwfs3ema3hi9s%26oauth_nonce%3D1887241367210%26oauth_signature_method%3DHMAC-SHA1%26oauth_timestamp%3D1331326503%26oauth_token%3D8b83142a-d5a6-452e-80ef-6e75b1b0ce18%26oauth_verifier%3D94828%26oauth_version%3D1.0
When sending a POST request, you need to put the authentication information in the header, not in the query parameters.
See this page for information (look for "Sending an Authorization Header"):
https://developer.linkedin.com/documents/common-issues-oauth-authentication
I suspect this is the issue you're running into.
Okay, so it was a stupid answer, but the problem was that I didn't see the oauth_token_secret come in when the user allowed access to my app, so I was still trying to sign the request using only the consumer secret and not both the consumer secret and oauth token secret.