I'm trying to run unit tests in Python for my flask application for routes that depend on the userID which is obtained from the access_token.
Is there a way to call the auth0 authorize API, in Python, to obtain an access_token for a user given their username and password?
If not, then what is an automated way of calling the authorize API to give it a username and password and obtain an access_token?
A code snippet would be best.
Thanks to #Jerdog, I've constructed the required piece of code:
import json
import requests
# testing user password database:
testingUsers = {
'testingUser2#funnymail.com': 'BuQ3tUS3 :jbFAL',
'testingUser3w#funnymail.com': 'y(1726854(b(-KY'
}
def getUserToken(userName):
# client id and secret come from LogIn (Test Client)! which has password enabled under "Client > Advanced > Grant Types > Tick Password"
url = 'https://YOUR_AUTH0_DOMAIN/oauth/token'
headers = {'content-type': 'application/json'}
password = testingUsers[userName]
parameter = { "client_id":"Jfjrl12w55uqcJswWmMhSm5IG2Qov8w2e",
"client_secret": "3E5ZnqLFbPUppBLQiGDjB0H2GtXaLyaD26sdk2HmHrBXQaDYE453UCUoUHmt5nWWh",
"audience": 'AUTH0_AUDIENCE',
"grant_type": "password",
"username": userName,
"password": password, "scope": "openid" }
# do the equivalent of a CURL request from https://auth0.com/docs/quickstart/backend/python/02-using#obtaining-an-access-token-for-testing
responseDICT = json.loads(requests.post(url, json=parameter, headers=headers).text)
return responseDICT['access_token']
#memoize # memoize code from: https://stackoverflow.com/a/815160
def getUserTokenHeaders(userName='testingUser2#funnymail.com'):
return { 'authorization': "Bearer " + getUserToken(userName)}
The #memoize decorator is to avoid multiple calls to get a token over many tests. The tenant has to have a default database specified for the above call to work (see this answer). It was a bit cryptic as to what the database name was supposed to be (the default_directory), but for me, with only Auth0 users, the database was Username-Password-Authentication, which seems to be the default for new accounts.
Have you looked at the https://auth0.com/docs/quickstart/backend/python/01-authorization walkthrough? The full quickstart for Python should give you a good start
Related
I want to list/search all the issues in Jira. I have a code like :
url = 'https://company.com/rest/api/2/search'
auth = HTTPBasicAuth("username", "password") // I tries token as well
headers = {
'Accept': 'application/json'
}
query = {
'jql': 'project=PRKJECTKEY',
'startAt': 0
}
response = requests.request(
"GET",
url,
headers=headers,
auth=auth,
params=query
)
I am not sure if the password should be token or the actual password and the url should be should be starting from companyname.com. This gives me <Response [401]> but i have all the permissions with the account.
Can someone help me with the authentication is supposed to be used this way.
I can only describe my way of accessing the JIRA-API:
1. I am using an API-key for this which one can easily create online, if one has the necessary permissions
(https://developer.atlassian.com/cloud/jira/platform/basic-auth-for-rest-apis/)
2. You need to set up the Jira-object to query issues first
user = 'firstname.familyname#account.xx'
apikey = 'xxxxxxxxxxxxxxxxx'
server = 'https://companypage.atlassian.net'
options = {
'server': server
}
jira = JIRA(options, basic_auth=(user, apikey))
3. Now, you can use the Jira-object to query with
tickets = jira.search_issues('text ~ "my search text" ORDER BY updated DESC')
Now, you can look what you got back and play with the results
for ticket in tickets:
print(ticket)
For Packages, you only need to import JIRA at the top (obviously, need to install jira first):
from jira import JIRA
Well, why don't you use atlassian-python-api?
https://community.atlassian.com/t5/Jira-articles/Atlassian-Python-API-s/ba-p/2091355
It's much easier to work with Jira via their own library. I have been working with Confluence via this and it's pretty simple. Take a look it may solve your problem.
EDIT: here is documentation. https://atlassian-python-api.readthedocs.io/
I am trying to create an API for our organization using FastAPI. It has a KeyCloak server that is used for all authentication, and OpenID Connect and JWTs in the way that is considered best practice.
In the simplest case, someone else takes care of acquiring a valid JWT token so that FastAPI then can simply decode and read the user and permissions.
oauth2_scheme = OAuth2PasswordBearer(tokenUrl="token")
async def get_current_user(token: str = Depends(oauth2_scheme)):
credentials_exception = HTTPException(
status_code=status.HTTP_401_UNAUTHORIZED,
detail="Could not validate credentials",
headers={"WWW-Authenticate": "Bearer"},
)
try:
jwt_token = jwt.decode(token, key=env.keycloak_server_public_key, audience='myorg')
return jwt_token['preferred_username']
except jwt.exceptions.ExpiredSignatureError:
raise credentials_exception
Life is simple!
I do, however, want to try to let users explore the API using the Swagger page. I have created this function that lets users login using the UI:
#app.post("/token")
async def login(form_data: OAuth2PasswordRequestForm = Depends()):
login_request = requests.post(
"https://mygreatorg.com/auth/realms/master/protocol/openid-connect/token",
data={
"grant_type": "password",
"username": form_data.username,
"password": form_data.password,
"client_id": "fastapi-application",
},
)
raw_response = json.loads(login_request.content.decode('utf-8'))
raw_response['acquire_time'] = time.time()
TOKEN_CACHE[form_data.username] = raw_response
return {"access_token": raw_response['access_token'], "token_type": "bearer"}
This works fine. The auth header in Swagger is now the token, and it validates, for about a minute. The expire time for the tokens is set to a very short time. One is then expected to refresh them using the refresh_token provided in the raw_response payload.
I can very easily make another request to get a new valid access token given the refresh_token. But I am unable to get Swagger to change the token of the request in the UI. The only way I find is to log out and log in again, but users will be very annoyed if they only allow a minute without being kicked out.
One workaround would be to simply cache the token and ignore the expiration time and let the user be logged in for a while longer, but that defeats the purpose of the entire security setup and feels like a bad idea.
Any ideas on how to let the UI of FastAPI update the bearer token when it needs a refresh, without letting the user log in again?
This is far from an answer and I will likely delete this later. It is a only a placeholder for outlining my research into this question
USE CASE: Swagger UI need to auto refresh with updated JWT token without closing the UI.
Systems/Applications:
KeyCloak
FastApi
Swagger
OpenID Connect and JWTs
When I looking into this question I noted that the issue in this question was raised in the issues at both FastApi and Swagger.
Swagger
When looking through the code base of Swagger, I noted an authorization parameter named persistAuthorization. According to the documentation this parameter will maintain authorization data and this data will not be lost on browser close/refresh.
In the Swagger code base I see this item:
# source: /src/plugins/topbar/topbar.jsx
#
flushAuthData() {
const { persistAuthorization } = this.props.getConfigs()
if (persistAuthorization)
{
return
}
this.props.authActions.restoreAuthorization({
authorized: {}
})
}
The code above makes calls to /src/core/plugins/auth/actions.js.
In the Swagger pull requests there is a pending feature named configs.preserveAuthorization This scope of this feature:
Refreshing or closing/reopening the the page will preserve authorization if we have configs.preserveAuthorization set to true.
It's unclear based on the comments how the features preserveAuthorization and persistAuthorization are different.
FastApi
When I was looking at the FastApi code base, I noted a OAuthFlowsModel named refreshUrl. I looked through the FastApi document and didn't see this mentioned.
# source: /fastapi/security/oauth2.py
#
class OAuth2AuthorizationCodeBearer(OAuth2):
def __init__(
self,
authorizationUrl: str,
tokenUrl: str,
refreshUrl: Optional[str] = None,
scheme_name: Optional[str] = None,
scopes: Optional[Dict[str, str]] = None,
auto_error: bool = True,
):
if not scopes:
scopes = {}
flows = OAuthFlowsModel(
authorizationCode={
"authorizationUrl": authorizationUrl,
"tokenUrl": tokenUrl,
"refreshUrl": refreshUrl,
"scopes": scopes,
}
)
super().__init__(flows=flows, scheme_name=scheme_name, auto_error=auto_error)
async def __call__(self, request: Request) -> Optional[str]:
authorization: str = request.headers.get("Authorization")
scheme, param = get_authorization_scheme_param(authorization)
if not authorization or scheme.lower() != "bearer":
if self.auto_error:
raise HTTPException(
status_code=HTTP_401_UNAUTHORIZED,
detail="Not authenticated",
headers={"WWW-Authenticate": "Bearer"},
)
else:
return None # pragma: nocover
return param
Looking through the issues for FastApi I noted one asking for OAuth2RerefreshRequestForm to be added. The scope of this issue is token refresh.
I also noted another issue refresh token with Keycloak and Fastapi
I apologize for not being able to provide a solid answer.
I can't create new repositoies in bitbucket cloud with code below.
I'm able to delete repositiries (with change form 'post' to 'delete' requests method). When I'm using code below I've got http 400 which means according to api docs - If the input document was invalid, or if the caller lacks the privilege to create repositories under the targeted account.
import requests
username = 'user#mail.com'
password = 'password'
headers = {"Content-Type": 'application/json'}
auth = (username, password)
bb_base_url = f"https://api.bitbucket.org/2.0/repositories/username/reponame"
res = requests.post(bb_base_url, headers=headers, auth=auth)
print(res)
So would like to ask for help to refactor code in way I will be able to do two things
I am trying to make a request to the Proactive Events API by using the requests module of Python.
However I always receive a response that the scope is invalid.
Can anyone help? What am I doing wrong? My code looks like this:
#ask.launch
def launch():
content_type = "application/x-www-form-urlencoded;charset=utf-8"
client_id = "amzn1.application-oa2-client.6a48XXXXXXX408"
client_secret = "592XXXXxxxxxxx6"
scope = "alexa::proactive_events"
grant_type = "client_credentials"
data = {"grant_type": grant_type, "client_id": client_id, "client_secret": client_secret, "scope": scope}
r = requests.post("https://api.amazon.com/auth/O2/token", data=data, headers={"content-type": content_type})
speech = render_template("welcome")
reprompt = render_template("welcome_reprompt")
return question(speech).reprompt(reprompt)
That is the response I get:
{'error_description': 'The request has an invalid parameter : scope', 'error': 'invalid_scope'}
Since one of the reason you get the invalid scope is that you dont have the events included in your skill manifest I include some steps here. I found quite cumbersome to use the SMAPI to update the skill manifest so instead I used ask cli.
install ask-cli: get authorization code for your amazon acount. In my case the backend is not an AWS lambda function but an external web server
get the skill manifest in json format:
ask api get-skill -s "amzn1.ask.skill.ZZZYYYZZ" --stage development > skill.json
Add the notifications permission and the events elements to the manifest:
{
"name": "alexa::devices:all:notifications:write"
}
and
"events": {
"publications": [
{
"eventName": "AMAZON.AAABBBCC"
}
],
"endpoint": {
"uri": "https://XXXYYYZZ:443/whatevercontext"
}
}
update the manifest:
ask api update-skill -s "amzn1.ask.skill.ZZZYYYZZ" --stage development -f skill.json
enable the notifications in the alexa app for your specific skill
Now you should be able to get the token and next step is to send the notification to the device
Have you tried making the API call via any other method? I just tried that with Postman and it worked for me.
My Python's a bit rusty, but here's the self generated code from Postman for Python. May be this should help?
import http.client
conn = http.client.HTTPConnection("api,amazon,com")
payload = "grant_type=client_credentials&client_id=amzn1.application-oa2-client.whatever-value&client_secret=client-secret&scope=alexa%3A%3Aproactive_events&undefined="
headers = {
'Content-Type': "application/x-www-form-urlencoded"
}
conn.request("POST", "auth,O2,token", payload, headers)
res = conn.getresponse()
data = res.read()
print(data.decode("utf-8"))
As Karthik asked previously have you tried the call via postman? I first suggest trying to via postman before you trying to code it.
If the issue still persists its most likely because you haven't included the necessary permissions required for Proactive Events in your Skill Manifest.
To add the necessary permissions to Skill Manifest you need to use the Skill Management API & ASK Cli.
Follow this section of the documentation and visit the links referenced there to correctly add the required permissions to your skill - https://developer.amazon.com/docs/smapi/proactive-events-api.html#onboard-smapi
Once you have successfully added the events and publications with a skill schema you should be able to successfully generate a token.
Please feel to ask if you want me to elaborate more on the exact steps.
Cheers!
This is what I have tried and it worked:
amazon_token_url = "https://api.amazon.com/auth/O2/token"
headers = {'Content-Type': 'application/x-www-form-urlencoded;charset=UTF-8'}
body = "grant_type=client_credentials&client_id=amzn1.application-oa2-client.XXXYYYZZ&client_secret=ZZZYYYXXX&scope=alexa::proactive_events"
log.debug("Sending token request with body: %s", body)
resp = requests.post(amazon_token_url, data=body, headers=headers)
resp_json = json.loads(resp.content.decode('utf-8'))
if (resp.status_code < 299) and (resp.status_code > 199):
log.debug("token received from Amazon")
log.debug("Content : %s", resp.content)
token = resp_json["access_token"]
return token
Quick question: I'm trying to use the Discord API to make a backup of all the messages on a server (or a guild, if you use the official term).
So I implemented OAuth without any problems, I have my access token and I can query some endpoints (I tried /users/#me, /users/#me/guilds). Though, most of them don't work. For example, if I query /users/#me/channels (which is supposed to be the DMs) I get a 401 Unauthorized response from the API. It's the same if I gather a guild id from /users/#me/guilds and then try to list the channels in it with /guilds/guild.id/channels.
The really weird thing is that I do have all the scopes required (I think so, I didn't take the RPC ones since I don't think it's required for what I want to do) and I can't figure it out myself... What is also weird is that on the OAuth authorization screen, I have those two things:
It kind of counterdicts itself... :(
Do you have any ideas you'd like to share ?
Thanks!
Note: I'm using Python but I don't think it's related here, since some endpoints do work with the headers and tokens I have...
Here is my "authentication code":
baseUrl = "https://discordapp.com/api"
def authorize():
scopes = [
"guilds",
"email",
"identify",
"messages.read",
"guilds.join",
"gdm.join",
"connections"
]
urlAuthorize = "{}/oauth2/authorize?client_id={}&scope={}&response_type=code".format(baseUrl, clientid, ('+'.join(scopes)))
pyperclip.copy(urlAuthorize)
code = input("Code: ")
return code
def getAccessToken(code):
url = "{}/oauth2/token".format(baseUrl)
params = {
"client_id" : clientid,
"client_secret" : clientsecret,
"redirect_uri" : "http://localhost",
"grant_type":"authorization_code",
"code" : code,
}
req = requests.post(url, params = params)
return json.loads(req.text)
And the code related to an API request:
def getHeaders():
return {
"Authorization" : "{} {}".format("Bearer", config["accessToken"]),
# "user-agent" : "DiscordBackup/0.0.1"
}
def getRequest(endpoint, asJson = True, additional = None):
url = "{}/{}".format(baseUrl, endpoint)
req = requests.get(url, headers = getHeaders())
print()
print(getHeaders())
print(url)
print(req.text)
if asJson:
return json.loads(req.text)
else:
return req.text
def getMe(): # this works
endpoint = "users/#me"
return getRequest(endpoint)
def getMyDMs(): # this gives me a code 401 Unauthorized
endpoint = "/users/#me/channels"
return getRequest(endpoint)
I came across this post when encountering this issue, and to put it bluntly, there's no way to resolve it.
The messages.read permission is for a local RPC server; https://discordapp.com/developers/docs/topics/oauth2#shared-resources-oauth2-scopes
However, local RPC servers are in private beta and you must sign up/get accepted to use this.
I wanted to create a DM exporter, but that doesn't look likely now.