I am writing a program that can change your Mojang username at whatever time you specify (i.e. the name "tom" becomes available at 09:05:39 so you would set the program to change your name to "tom" at that time). To do this you need a bearer token. I can log into minecraft.net and use EditThisCookie to access that, but I want to be able to have the program wait until it is within a minute of the name dropping to gather proxies and the bearer token automatically for situations where a name is becoming available in the middle of the night or I'm just lazy. I'm not sure why my function for getting the bearer token won't work. I referenced this to write this code.
def getAT():
jsonForAT = json.dumps({"agent":{"name":"Minecraft","version":1},"username":email,"password":password,"clientToken":""})
headersForAT = {'Content-Type': 'application/json'}
requestForAT = requests.post('https://authserver.mojang.com/authenticate', data=jsonForAT, headers=headersForAT)
pullATRequestData = requestForAT.json()
AT = pullATRequestData["accessToken"]
return AT
I am really confused since when I use this, I get a 401 error but the program works when I get the token manually.
I also was experiencing the same issue. Then, I realized that Minecraft/Mojang accounts have security questions. If your function is getting the bearer token and your issue is that your access is denied when posting a name change request:
1st make sure your formatting it properly:
headers={'Authorization': f"Bearer {TOKEN}"
Notice the space between "Bearer" and {TOKEN}
2nd: For some reason, Mojang needs you to send a Get request to their security challenges endpoint. You don't have to do anything with the returned data..
When you got the token manually, I'm assuming your browser automatically sent this get request and that's why it worked. If you're using proxies, you'll probably need to send a post request with your security question answers, look at the API link for documentation.
For Example:
res = requests.get(
"https://api.mojang.com/user/security/challenges",
headers={"Authorization": f"Bearer {TOKEN}"},
)
https://wiki.vg/Mojang_API
Go to the bottom under "Security question-answer flow"
Related
I want to connect to Netsuite via Token-Based Authentication (i.e. OAuth1) as documented here.
In the section Step One Obtain An Unauthorized Request Token it is written that an optional state parameter can be added to the Authorization Header. There they also refer to RFC 6749, Section 4.1.1 for further information. However, what is explained there has nothing to do with OAuth1.0 but Oauth2.0.
The reason why I depend on the state parameter is that I have the url to which the callback server shall forward the request after the authorization is done encoded in it (using JWT).
Now when I create the OAuth1 authorization header with oauthlib using the sign method from oauthlib.oauth1.Client in Python
from oauthlib.oauth1 import SIGNATURE_HMAC_SHA256
from oauthlib.oauth1 import Client
client = Client(client_key=CONSUMER_KEY,
client_secret=CONSUMER_SECRET,
callback_uri=CALLBACK_URL,
signature_method=SIGNATURE_HMAC_SHA256)
uri, headers, body = client.sign(uri="https://123456.restlets.api.netsuite.com/rest/requesttoken", http_method='POST')
I get this for headers:
headers = {
'Authorization': 'OAuth oauth_nonce="123..", oauth_timestamp="163...", oauth_version="1.0", oauth_signature_method="HMAC-SHA256", oauth_consumer_key="f18...", oauth_callback="...", oauth_signature="9kae..."'
}
I can acquire the temporary credentials when sending the request with this headers to https://123456.restlets.api.netsuite.com/rest/requesttoken.
Still as I need the state parameter later on on my side I somehow need to add the state parameter to the authorization header (at least that is what Netsuite says in their documentation).
When I add my state parameter to the authorization header (the one created before by the sign method from oauthlib.oauth1.Client) like this
from oauthlib.common import to_unicode
headers["Authorization"] = f'{headers["Authorization"]}, state="{to_unicode(data=state, encoding="UTF-8")}"'
which results in this for headers (I will refer to it as new_headers):
# headers with state appended to Authorization
headers = {'Authorization': 'OAuth oauth_nonce="123...", oauth_timestamp="163...", oauth_version="1.0", oauth_signature_method="HMAC-SHA256", oauth_consumer_key="f18...", oauth_callback="...", oauth_signature="9kae...", state="eyJ0..."'}
I get this response when trying to send a request to the request token url with this header:
{"error" : {"code" : "USER_ERROR", "message" : "Invalid login attempt."}}
When I do it differently (not as specified in the doc) and add the state parameter to the request token url like this "https://123456.restlets.api.netsuite.com/rest/requesttoken?state=eyJ0..." and send the previous header with authorization not including the state (i.e. headers) I again get to the login page. So I can assume this could work.
My problem is that I cannot test it at the moment with a Netsuite account so I just need to implement it according to the documentation and hope that I send the state parameter in the right way and it is forwarded to the callback server after a user logs in.
Now my question is:
Is the documentation correct and the state parameter needs to be added to the authorization header like in "new_headers" above and I just do sth wrong here. If so what am I doing wrong here?
Or is the documentation misleading as simply adding the state parameter as a normal query parameter to the request token url like this "https://123456.restlets.api.netsuite.com/rest/requesttoken?state=eyJ0..." is correct?
I would really appreciate some help here!
Best regards,
JayKay
I have attempted to follow the SurveyMonkey API documentation to a tee, just to get familiar with it before I move into the very simple functionality of my private application.
I have pretty much attempted to duplicate exactly what I've found in the documentation, but am getting a 403 response. You can see the exact snipped of code here in the docs: https://api.surveymonkey.net/v3/docs?python#surveys-id
api_token = 'MyVerySecretApiKey'
survey_id = 'ASillyLittleNumber'
s.headers.update ({
"Authorization": "Bearer %s" % api_token,
"Content-Type": "application/json"
})
HOST = "https://api.surveymonkey.com/v3/surveys/%s" % (survey_id)
print s.get(HOST)
I've printed the GET just to see what exactly I'm getting, and I'm getting Response [403]. I can't figure out what I've done, especially with such a simple application. I've checked my api token and the survey id several time for accuracy. Is there anything I'm not understanding about the code from the documentation?
I've passed my api key and survey id as strings, and that's correct right?
Your code looks fine - the requests module will take care of integers / strings in your headers, no need to worry about that.
The only thing I can think of is that maybe you're confusing your API key with your bearer token - based on this line:
api_token = 'MyVerySecretApiKey'
Check the settings page on your SM dev page and see if you can generate a bearer token for your account, and if it differes from the API key.
Scenario
A logged in user will have a token expiry of 24 hours. Within that period, all request with #jwt_required decorator will have the current access token's expiry extended by another 24 hours. There is a maximum validity of 168(24 * 7) hours.
It is possible to use access_token and refresh_token.
ret = {
'access_token': create_access_token(identity=username, fresh=True),
'refresh_token': create_refresh_token(identity=username)
}
But that means every API call from my applicatino will be two requests:
1. Actual HTTP Request
2. Refresh the auth token
#app.route('/refresh', methods=['POST'])
#jwt_refresh_token_required
def refresh():
current_user = get_jwt_identity()
ret = {
'access_token': create_access_token(identity=current_user)
}
return jsonify(ret), 200
Is there a way to implicitly extend an auth token?
EDIT: There is now documentation around this here: https://flask-jwt-extended.readthedocs.io/en/latest/refreshing_tokens/
Author of flask-jwt-extended here. Technically you cannot actually extend a token, you can only replace it with a new JWT that has a new expires time. There are a few ways you could simulate this though.
First, instead of having the client request a new token, you could have the server itself just implicitly send back a new token on every request. You could send the new JWTs back in a header instead of in the JSON payload, so that you wouldn't have to modify you JSON data to account for the possibility of a new JWT. Your clients would need to be aware of this though, they would need to check for that new header on every request and replace their current JWT with the new one if it is present. You could probably use a flask after_request method to do this, so you didn't have to add that functionality to all your endpoints. A similar effect could be achieved when storing the JWTs in cookies, with the differences being that cookies are automatically stored in your browser (so your client wouldn't have to manually look for them on every request), and with the added complexity of CSRF protection if you go this route (http://flask-jwt-extended.readthedocs.io/en/latest/tokens_in_cookies.html).
The above should work fine, but you will be creating a lot of access tokens that are thrown away right after being created, which probably isn't ideal. A variation of the above is to check if the token is near expiring (maybe if it is more then half way to being expired) and only create and return a new token if that is the case. Another variation of this would be to have the client check if the token is about to expire (via javascript) and if it is, use the refresh token to request a new access token. To do that, you would need to split the JWT on dots ('.'), base64 decode the second set of strings from that split (index 1), and grab the 'exp' data from there.
A second way you could do this is actually wait for a token to expire, and then use the refresh token to generate a new access token and remake the request (reactive instead of proactive). That might look like making a request, checking if the http code is 401, if so use the refresh token to generate a new access token, then making the request again.
Hope this helps :)
app = Flask(__name__)
app.config["JWT_SECRET_KEY"] = "super-secret" # Change this!
app.config["JWT_ACCESS_TOKEN_EXPIRES"] = timedelta(hours=1)
app.config["JWT_REFRESH_TOKEN_EXPIRES"] = timedelta(days=30)
jwt = JWTManager(app)
change time according to your requirement
I'm trying to make a request to the particle servers in python in a google app engine app.
In my terminal, I can complete the request simply and successfully with requests as:
res = requests.get('https://api.particle.io/v1/devices', params={"access_token": {ACCESS_TOKEN}})
But in my app, the same thing doesn't work with urlfetch, which keeps telling me it can't find the access token:
url = 'https://api.particle.io/v1/devices'
payload = {"access_token": {ACCESS_TOKEN}}
form_data = urllib.urlencode(payload)
res = urlfetch.fetch(
url=url,
payload=form_data,
method=urlfetch.GET,
headers={
'Content-Type':
'application/x-www-form-urlencoded'
},
follow_redirects=False
)
I have no idea what the problem is, and no way to debug. Thanks!
In a nutshell, your problem is that in your urlfetch sample you're embedding your access token into the request body, and since you're issuing a GET request -which cannot carry any request body with them- this information gets discarded.
Why does your first snippet work?
Because requests.get() takes that optional params argument that means: "take this dictionary I give you, convert all its key/value pairs into a query string and append it to the main URL"
So, behind the curtains, requests.get() is building a string like this:
https://api.particle.io/v1/devices?access_token=ACCESS_TOKEN
That's the correct endpoint you should point your GET requests to.
Why doesn't your second snippet work?
This time, urlfetch.fetch() uses a different syntax than requests.get() (but equivalent nonetheless). The important bit to note here is that payload argument doesn't mean the same as our params argument that you used before in requests.get().
urlfetch.fetch() expects our query string -if any- to be already urlencoded into the URL (that's why urllib.urlencode() comes into play here). On the other hand, payload is where you should put your request body in case you were issuing a POST, PUT or PATCH request, but particle.io's endpoint is not expecting your OAuth access token to be there.
Something like this should work (disclaimer: not tested):
auth = {"access_token": {ACCESS_TOKEN}}
url_params = urllib.urlencode(auth)
url = 'https://api.particle.io/v1/devices?%s' % url_params
res = urlfetch.fetch(
url=url,
method=urlfetch.GET,
follow_redirects=False
)
Notice how now we don't need your previous Content-type header anymore, since we aren't carrying any content after all. Hence, headers parameter can be removed from this example call.
For further reference, take a look at urlfetch.fetch() reference and this SO thread that will hopefully give you a better insight into HTTP methods, parameters and request bodies than my poor explanation here.
PS: If particle.io servers support it (they should), you should move away from this authentication schema and carry your tokens in a Authorization: Bearer <access_token> header instead. Carrying access tokens in URLs is not a good idea because they are much more visible that way and tend to stay logged in servers, hence posing a security risk. On the other hand, in a TLS session all request headers are always encrypted so your auth tokens are well hidden there.
Ok, so, as it turns out, one cannot include a payload for a GET request using Urlfetch. Instead, one has to include the parameters in the url using the '?' syntax as follows:
url = 'https://api.particle.io/v1/devices'
url = url + '?access_token=' + {ACCESS_TOKEN}
res = urlfetch.fetch(
url=url,
method=urlfetch.GET,
follow_redirects=False
)
this worked for me.
I'm working on an app which uses an API that requires me to make a first Post request to authenticate.
Looking the authenticate response, I've seen that a cookie was created: ApiCredentials=....
So I authenticate:
result = urlfetch.fetch(url = url, method = urlfetch.POST)
api_credential = result.headers['set-cookie']
and then I create a request with that cookie in the header
urlfetch.fetch(url = url, method = urlfetch.GET, headers = {'Cookie': api_credential})
The problem is: in dev everything works perfectly, but when I deploy, it doesn't work. In the logs I can see the cookie that was recieved.
API link: http://www.sptrans.com.br/desenvolvedores/APIOlhoVivo/Documentacao.aspx?1 (portuguese)
The code in the question does not show the cookie name ApiCredentials. It may be that in development, there was only a single cookie and in production there are more, in which case result.headers['set-cookie'] returns multiple values comma separated.
The URL Fetch Response Objects page suggests retrieving multiple headers of the same name in a list by calling result.header_msg.getheaders('set-cookie') instead and then finding ApiCredentials in the resulting list.
It might be safer to say 'Set-Cookie' in case-sensitive environments.