I would like to verify an Okta token (RS256) in python. I have tried to find the solution online but have not managed to get it to work:
I have tried this so far:
from py_jwt_validator import PyJwtValidator, PyJwtException
jwt = OKTA_TOKEN
try:
PyJwtValidator(jwt)
except PyJwtException as e:
print(f"Exception caught. Error: {e}")
throwing the error:
Okta-Specific: Access Tokens can not be validated locally without a Custom Authorization Server.
I have also tried to use the okta endpoint /introspect, but I keep on getting Response 405. I try to do get request to introspect/client_id=CLIENT_ID&token_type_hint=access_token&token=TOKEN
I also tried several other packages including okta_jwt (complains about ErrorKey : "jwks_uri")...
I found an answer to my own question. I can use the /introspect endpoint of okta:
headers = {"Content-type": "application/x-www-form-urlencoded"}
http = urllib3.PoolManager()
url = ".../oauth2/v1/introspect/"
token = TOKEN
client_id = YOUR_CLIENT_ID
data = "client_id={}&token_type_hint=access_token&token={}".format(client_id, token)
response = http.request('POST', url, body=data, headers=headers)
decoded_response = response.data.decode('utf-8')
json_response = json.loads(decoded_response)
json_response["active"] will then be true if the token has been validated and false if its not the case.
Related
We have a use case in which Admin can set Google Or Microsoft's Apps Client Id and Secrets that can later be used for all users who sign up with this Admin's link, Is there a way to verify these fields before saving/using them?
like for Microsoft,How do I verify this info that admin is providing? Is there any API call to verify it?
Update:
I am able to do this for G suite, what I did was:
1- Create a request to get token from client id and secret, (with wrong code).
2- In response, google returns 'invalid_client' in case the credentials are wrong and 'invalid_grant' in case the credentials are correct. (because Code is wrong too.)
try:
discovery_url = settings.GOOGLE_DISCOVERY_URL
callback_url = settings.BASE_URL + "/accounts/invite/google/signup/callback/"
client = WebApplicationClient(client_id)
identity_provider_config = requests.get(
discovery_url,
).json()
token_endpoint = identity_provider_config["token_endpoint"]
token_url, headers, body = client.prepare_token_request(
token_endpoint, redirect_url=callback_url, code='**code'**
)
token_response = requests.post(
token_url,
headers=headers,
data=body,
auth=(client_id, client_secret),
).json()
# Parse the tokens!
client.parse_request_body_response(json.dumps(token_response))
except Exception as error:
json_object = json.loads(error.json)
pairs = json_object.items()
if list(pairs)[0][1] == 'invalid_grant':
return "Your credentials are correct"
if list(pairs)[0][1] == 'invalid_client':
return "Your credentials are NOT correct"
FOR MICROSOFT:
For microsoft, additional tenant id is required in order to do this:
We did this in following way:
def validate_azure_credentials(client_id, client_secret, tenant_id):
"""
validate client id and secret from microsoft and google
"""
try:
app = msal.ConfidentialClientApplication(
client_id=client_id,
client_credential=client_secret,
authority=f"https://login.microsoftonline.com/{tenant_id}",
)
# call for default scope in order to verify client id and secret.
scopes = ["https://vault.azure.net/.default"]
token = app.acquire_token_for_client(scopes=scopes)
if token.get("access_token") is None:
return IdpResponseMessages.INVALID_CREDENTIALS
return IdpResponseMessages.VALID_CREDENTIALS
except Exception as error:
logger.debug(f"Exception {error}")
return str(error)
The following code throws a 401 error and the access_token does not get retrieved. It seems like https://www.reddit.com/api/v1/access_token is rejecting the username/password or clientID/Secretkey that I have provided. But all that info is correct, the username and password is absolutely same as the Reddit login details, and the client ID & Secret key are directly a copy-paste from a dev app created here: https://www.reddit.com/prefs/apps
Please suggest what could be wrong
import requests
auth = requests.auth.HTTPBasicAuth('<client_id>', '<client_secret>')
data = {'grant_type': 'password','username': 'username','password': 'password'}
headers = {'User-Agent': 'ApiTest/0.0.1'}
res = requests.post('https://www.reddit.com/api/v1/access_token',auth=auth, data=data, headers=headers)
if "Unauthorized" in res.text:
print('401 error occured')
TOKEN = res.json()['access_token']
headers = {**headers, **{'Authorization': f"bearer {TOKEN}"}}
requests.get('https://oauth.reddit.com/api/v1/me', headers=headers)
Try to change from bearer to Bearer in your headers. You can also remove the **.
Should be something like this:
headers = {'Authorization': f"Bearer {TOKEN}"}
In the API reference it is stated, that all parameters are optional, except for the project name.
But when I try this:
url = 'https://dialogflow.googleapis.com/v2beta1/projects/'+config["DFproject"]+'/agent/intents'
headers = {'Authorization': "Bearer "+access_token, 'content-type': 'application/json'}
r = requests.post(url, headers=headers)
I get:
{'error': {'code': 400, 'message': "Errors in '' intent: display_name is a required field.", 'status': 'INVALID_ARGUMENT'}}
I thought the display_name comes from the response.
So how am I supposed to set it?
This is the API: Google API
The error is you are using POST in your url. You should be using GET instead for retrievals.
If you want to play around with the API and cant seem to set up the authentication on Postman, feel free to use this : https://developers.google.com/oauthplayground/
and this for reference: https://cloud.google.com/dialogflow/docs/reference/rest/v2-overview
I have gone through number of similar posts related to firing GET requests with Basic Auth (eg: Python, HTTPS GET with basic authentication), still can't figure out the problem. I keep getting the error requests.exceptions.HTTPError: 401 Client Error: Unauthorized for url
With the same credentials, headers tried the same in postman it works as expected. Verified that base64encoded value for the api_key, password is exactly same as the value used in postman, so I don't think its encoding or resource access permission problem.
python -V
Python 3.6.4 :: Anaconda, Inc.
Approach 1
api_key = 'some_api_key'
password = 'some_password'
headers = {'accept': 'application/json'}
url = 'https://test.access.com/this/url'
api_key_password = "%s:%s" % (api_key, password)
b64_encoded = b64encode(bytes(api_key_password, 'utf-8')).decode("ascii")
headers['authorization'] = 'Basic %s' % b64_encoded
response = requests.get(url,
headers=headers)
if (response.ok):
json_data = json.loads(response.content)
print (json_data)
else:
print (response)
response.raise_for_status()
Approach 2
api_key = 'some_api_key'
password = 'some_password'
url = 'https://test.access.com/this/url'
headers = {
'accept': 'application/json',
}
response = requests.get(url, headers=headers, auth=(api_key, password))
print (response.ok)
if (response.ok):
json_data = json.loads(response.content)
print (json_data)
else:
print (response)
response.raise_for_status()
Can you please provide some pointers?
I had a similar issue (although in .NET Framework).
In my case the reason was that I was using the url without a forward slash in the end and the API apparently does not support that.
So https://test.access.com/this/url
Throws 401 error Unauthorized
but
https://test.access.com/this/url/
Returns 200 OK.
Older post but I had a similar issue. Postman will cache your JSESSIONID. Be sure you are clearing out that cookie while testing. If you are hitting an API that requires a login API call to establish a session before you can make subsequent API calls, this Postman behavior can produce a false sense of security.
In this situation with Python requests, it can be handled with code similar to what I've provided below:
import requests,json
loginAPI = "https://myapi.myco.comv/someuri/someuri/users/login"
someHTTPGetAPI = "https://myapi.myco.com/someuri/someuri/someservice"
username = "myuser"
password = "mypass"
headers = {
"Content-Type": "application/json",
"login": username,
"password": password
}
urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning)
verify=False
session = requests.Session()
sessionResponse = session.get(url=loginURL,headers=headers, verify=verify)
if sessionResponse.status_code == 200:
getResponse = session.get(url=someHTTPGetAPI)
if getResponse.status_code == 200:
responseJSON = agentStatus.json()
I'm trying to make a post request to Quizlet following their OAuth flow from these instructions https://quizlet.com/api/2.0/docs/authorization-code-flow. I'm running into a problem where on Step 2, I have to make a post request with a token I generated from their server, but I'm not having success passing in the token to the url. I know it was generated correctly, but I'm having trouble passing it in and not getting a 400 response.
More directly, my question is, is there another way of including the grant_type and code parameters that I'm trying to pass in through the url in the post request such as passing them in through the header of the post request? I've looked at the documentation for requests but I've had no luck.
#app.route('/')
#app.route('/index')
def index():
code = request.args.get('code')
state = request.args.get('state')
print("code is " + code)
r = requests.post("https://api.quizlet.com/oauth/token?grant_type=authorization_code&code=" + code)
return render_template('index.html')
You must specify the required headers Authorization, Content-Type.
import requests
from requests.auth import _basic_auth_str
client_id = 'YOUR CLIENT ID'
secret = 'YOUR CLIENT SECRET'
code = 'CODE FROM STEP 1'
headers = {
'Authorization': _basic_auth_str(client_id, secret),
'Content-Type': 'application/x-www-form-urlencoded'
}
r = requests.post('https://api.quizlet.com/oauth/token?grant_type=authorization_code&code={0}'.format(
code), headers=headers)
print r.status_code
print r.content