I am trying to have an Ouath2 access token based authentication. I am using django-oauth-toolkit for that. I have registered my app on http://localhost:8000/o/applications.
However, When I tried to hit the URL http://localhost:8000/o/token/ from the react-app . I got 401.
Here is my the useEffect hook that I used for calling that URL :
useEffect(() => {
// axios.get("api/tests")
// .then((res)=>{
// setUsers(JSON.stringify(res.data))
// })
fetch("http://localhost:8000/o/token/", {
body: new URLSearchParams({
grant_type: 'client_credentials',
client_id: client_id,
client_secret: client_secret
}),
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Authorization": `Basic ${btoa(client_id+":"+client_secret)}`}
,
method: "POST"
}).then((res)=>{
console.log(res)
})
}, [])
Will look forward to some comments and solutions.
I got the solution. Actually I was Using the encrypted version of the client secret. I forgot to copy it before saving. Once it is saved the value is encrypted. So it's better to copy it somewhere prior saving it.
Related
Server-side flask
#project_ns.route('/projects')
class ProjectsResource(Resource):
#project_ns.marshal_list_with(project_model)
#jwt_required()
def get(self):
"""Get all projects """
user_id = User.query.filter_by(username=get_jwt_identity()).first() # Filter DB by token (username)
projects=Project.query.filter_by(user_id=user_id)
#projects = Project.query.all()
return projects
client-side reactjs
const getAllProjects=()=>{
const token = localStorage.getItem('REACT_TOKEN_AUTH_KEY');
console.log(token)
const requestOptions = {
method: 'GET',
headers: {
'content-type': 'application/json',
'Authorization': `Bearer ${JSON.parse(token)}`
},
body: "Get projects listed"
}
fetch('/project/projects', requestOptions)
.then(res => res.json())
.then(data => {
setProjects(data)
})
.catch(err => console.log(err))
}
I specified header on the client side and the error still occurs:
flask_jwt_extended.exceptions.NoAuthorizationError: Missing Authorization Header
my flask versions are as follows:
Flask==2.0.1
Flask-Cors==3.0.10
Flask-JWT-Extended==4.2.3
Flask-Migrate==3.1.0
Flask-RESTful==0.3.9
flask-restx==0.5.0
Flask-SQLAlchemy==2.5.1
I have tried many options and the issue is still persisting. Would love to come to a resolution. Thanks in advance!
I haven't used these libraries before, but it sounds like it might possibly be an issue with the Flask-RESTful library?
https://stackoverflow.com/a/52102884/2077884
https://github.com/vimalloc/flask-jwt-extended/issues/86#issuecomment-335509456
https://github.com/vimalloc/flask-jwt-extended/issues/86#issuecomment-444983119
If you're not getting a value for console.log(token), I would start there. And maybe check to see if you can decode the token details via https://jwt.io/ to ensure that the value you have for token is valid.
In the frontend, I have the following JS function:
export const uploadFormData = async (
token: string,
email: string,
formInfo: Array<Object>,
): Promise<any> => {
const formData = new FormData();
formData.append('email', email);
formData.append('form_info', JSON.stringify({ formInfo }));
return fetch(
`${process.env.ENDPOINT}/upload_form_data/`,
{
method: 'POST',
headers: {
Authorization: `Token ${token}`,
},
body: formData,
},
).then((response) => {
console.log(response.body?.getReader());
if (response.status === 404) {
throw Error('Url not found');
}
if (response.status === 422) {
throw Error('Wrong request format');
}
if (response.status !== 200) {
throw Error('Something went wrong with uploading the form data.');
}
const data = response.json();
return {
succes: true,
data,
};
}).catch((error) => Promise.reject(error));
};
which sends a POST request to the following endpoint in the FastAPI backend:
#app.post("/api/queue/upload_form_data/")
async def upload_form_data(
email: str = Body(...),
form_info: str = Body(...),
authorization: str = Header(...),
):
return 'form data processing'
However, it keeps throwing the following errors:
In the frontend:
POST http://localhost:8000/api/queue/upload_form_data/ 422 (Unprocessable Entity)
Uncaught (in promise) Error: Wrong request format
In the backend:
POST /api/queue/upload_form_data/ HTTP/1.1" 400 Bad Request
In Swagger UI (response body):
{
"detail": [
{
"loc": [
"header",
"authorization"
],
"msg": "field required",
"type": "value_error.missing"
}
]
}
What is wrong with the request that is causing these errors?
In Swagger/OpenAPI specification, Authorization is a reserved header, along with Accept and Content-Type headers as well for Swagger's built-in authentication/authorization functionality—see Swagger documentation; hence, they are not allowed to be defined. If you are using Swagger, you can't have Authorization defined along with your endpoint's parameters, as it will be ignored when submitting the request through Swagger UI, and you'll get a 422 Unprocessable Entity error with a body message saying that the authorization header is miising (just like the error posted in your question).
Solutions
If you don't need Swagger UI for testing your application, you can leave it as is and keep using JavaScript Fetch API, passing the Authorization in the headers. Also, note that you don't really have to define any Header parameters in your endpoint, as you can always access them through the Request object, for instance:
from fastapi import Request
#app.post('/')
def main(request: Request):
token = request.headers.get('authorization')
return token
If you do need this to work with Swagger UI as well, one solution would be to rename the authorization Header parameter to something else, e.g., token: str = Header(...). Then, inside your endpoint check if the API key is in either the token or request.headers.get('authorization')—if both result to None, then it means no Authorization header was provided. Otherwise, use FastAPI's HTTPBearer, which would allow you to click on the Authorize button on the top right hand corner of your screen in Swagger UI autodocs at /docs, where you can type your API key in the Value field. This will set the Authorization header in the request headers. Example:
from fastapi.security import HTTPBearer
security = HTTPBearer()
#app.get('/')
def main(authorization=Depends(security)):
return authorization.credentials
Alternatively, you could use APIKeyHeader
from fastapi.security.api_key import APIKeyHeader
from fastapi import Security
api_key = APIKeyHeader(name='Authorization')
#app.get('/')
def main(token = Security(api_key)):
return token
I'm using signed url to get or post/put video fileq on my google storage. My modules refers on signed_url V4 and it's working quite well.
I wanted to add some metadata tags to my requests in order to manage more efficiently the charges related to GCS. But since I added those headers, the requests failed returning a cors policy error : (I have shortened the signature in the block above to make it more readable)
Access to XMLHttpRequest at 'https://test-dev-slotll.storage.googleapis.com/uploads/dave/FR/eaa678c9/2020/9/785f/f45d3d82-785f_full.mp4?X-
Goog-Algorithm=GOOG4-RSA-SHA256&X-Goog-Credential=dev-storage%40brilliant-tower-264412.iam.gserviceaccount.com%2F20200926%2Fauto%2Fstorage%2Fgoog4_request&X-Goog-Date=20200926T093102Z&X-Goog-Expires=3600&X-Goog-SignedHeaders=host%3Bx-goog-meta-account&x-goog-signature=6fbb27e[...]bd0891d21' from origin 'http://localhost:8000' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
and the detailed message:
<Error>
<Code>MalformedSecurityHeader</Code>
<Message>Your request has a malformed header.</Message>
<ParameterName>x-goog-meta-account</ParameterName>
<Details>Header was included in signedheaders, but not in the request.</Details>
</Error>
The cors are conigured to allow almost everything on my bucket :
[{"maxAgeSeconds": 3600, "method": ["*"], "origin": ["*"], "responseHeader": ["*"]}]
and here is the Python/Django function
def _get_signed_url(self, http_method="GET"):
"""' create a signed url with google storage
create a 'GET' request by default,
add method='w' or 'put' to create a 'PUT' request
get_signed_url('w')->PUT / get_signed_url()->GET
"""
if http_method.lower() in ["w", "put", "post"]:
http_method = "PUT"
else:
http_method = "GET"
signed_url = generate_signed_url(
settings.GS_BUCKET_NAME,
self.file.name,
subresource=None,
expiration=3600,
http_method=http_method,
query_parameters=None,
headers={'x-goog-meta-language':'french','x-goog-meta-account':'david',},
)
return signed_url
As I wrote it above, method get_signed_url() is copied from signed_url V4
if i replace headers = {'x-goog-meta-language':'french','x-goog-meta-account':'david',},
by hearders = {} or headers = None (as it was previously, it works fine
last, when I click on the link given by the signed-url, I got an error message:
The signed url as well as blob uploading or downloading are working fine without the headers for months but I do not see why the server is responding that the header meta tags are malformed...
I will appreciate any help
thanks !
I was getting the same error message when I was performing a GET request on the pre-signed urls from GCP. Adding the content-type: "application/octet-stream" solved it for me.
ajaxCall_getFile = $.ajax({
xhr: function() {
var xhr = new window.XMLHttpRequest();
return xhr;
},
type: 'GET',
url: <PRE-SIGNED URL>,
contentType: "application/octet-stream",
processData: false,
success: function(file_data){
alert("file downloaded");
},
error: function(err) {
alert('Download failed, please try again.');
}
});
As #Sideshowbarker suggests it in his comments, the problem came from the client.
The signed url was used to send files to storage through ajax but no custom header were added to the ajax.
By specifying the headers in the ajax, the PUT request of a signed url with custom metadata works well.
function ajaxSendToStorage(uuid, url) {
// Sending ONE file to storage
var file = getFileById(uuid);
$.ajax({
method: "PUT",
contentType: file.type,
processData: false,
dataType: "xml",
crossDomain: true,
data: file,
url: url,
headers: {"x-goog-meta-account":"david","x-goog-meta-language": "fr"},
success: function() {},
complete: function() {},
});
}
I want to read write value from a PLC through API (RESTful like) provide by constructor.
First step is authentification
Ajax code
$.ajax({
url: "http://192.168.10.11/sdcard/cpt/app/signin.php",
type: 'POST', dataType: 'json',data: {
'user[name]': 'admin',
'user[password]': 'hellocpt'
}
}).done(function(data) {
console.debug("signin done");
});
Next I can read value from JSON
var url='http://192.168.10.11/sdcard/cpt/app/data_api.php?
url=/app/objects/EasyIO/Ramp.out';
$.ajax(
{url: url, type: 'GET', dataType: 'json'}
).done(function(data) {console.debug(data);});
Response
{
"response" : {
"resultCode" : 0, // '0' for success, other numbers for
failure
"data" : [ // sedona object data is orgnized in list
{ // every object's data is a dictionary
"path" : "/EasyIO/Ramp", //sedona object's path
"slots" : [ //slots data is organized as a list
{
"value" : "49.000000", // 'out' slot's value
"slotType" : "property", // this slot is a
property
"name" : "out", // slot's name"type" : "float" // slot value's data type
}
]
}
]
}
}
Commande
var url = 'http://192.168.10.11/sdcard/cpt/app/data_api.php';
$.ajax({url: url,
type: 'POST',
dataType: 'json',
data: {
path: '/app/objects/EasyIO/WriteIn.in',
type: 'int',
value: '100',
slotType: 'property'
}
}).done(function(data) {
console.debug(data);
});
Response
{
"response" : {
"resultCode" : 0, // '0' for success, other numbers for
failure
"value" : "100", // slot value has been changed to '100'
"type" : "int", // slot data type is 'int'
"path" : "/app/objects/EasyIO/WriteIn.in" //slot's path
}
}
This API documentation
This my python3 code test
import os
import requests
import pycurl
import json
import urllib
from urllib.request import urlopen
headers = {
'X-Requested-With': 'XMLHttpRequest',
}
data = [
('user[name]', 'admin'),
('user[password]', 'hellocpt'),
]
response = requests.post('http://192.168.0.230/sdcard/cpt/app/signin.php', headers=headers, data=data)
print("Code Status du POST: ",response.status_code)
print(response.content)
cookies = {
}
params = (
('url', '/app/objects/EasyIO/Ramp.out'),
)
responseget = requests.get('http://192.168.0.230/sdcard/cpt/app/data_api.php', headers=headers, params=params, cookies=cookies)
print("\n\nCode Status du GET: ",responseget.status_code)
print(responseget.content)
and my output
Code Status du POST: 200
b'{"redirectUrl": "http://192.168.0.230/sdcard/cpt/dashboard/index.php" }'
Code Status du GET: 200
b'{"redirect": "http://192.168.0.230/sdcard/cpt/app/signin.php"}'
My problem is I don't understand why my POST command working but when I make a GET command I see redirection to signin page link. Why there is a logout?
Anyone can help?
Thanks
When you are sending your second request (GET), how will the server know that you have signed in before? The server sends you back something in the first request which you should send back to the server in your second request so the server can authenticate you.
I wasn't able to find out how your API is working using the ajax codes you have posted because the returned data is not shown in the first request but if we assume that the authentication is cookie based then instead of an empty cookie you should replace this line :
cookies = {
}
with this :
cookies = response.cookies
But this is only an example, your API might return a token instead of using cookies or some other type of authentication which is specified in the documentation.
EDIT : I was able to open the pdf file which contains the API documentation and it seems that the authentication is cookie based so the solution should work.
I am using Alamofire for the HTTP networking in my app. But in my api which is written in python have an header key for getting request, if there is a key then only give response. Now I want to use that header key in my iOS app with Alamofire, I am not getting it how to implement. Below is my code of normal without any key implementation:
Alamofire.request(.GET,"http://name/user_data/\(userName)#someURL.com").responseJSON { response in // 1
print(response.request) // original URL request
print(response.response) // URL response
print(response.data) // server data
print(response.result)
}
I have a key as "appkey" and value as a "test" in my api. If anyone can help. Thank you!
This should work
let headers = [
"appkey": "test"
]
Alamofire.request(.GET, "http://name/user_data/\(userName)#someURL.com", parameters: nil, encoding: .URL, headers: headers).responseJSON {
response in
//handle response
}
let headers: HTTPHeaders = [
"Accept": "application/json",
"appkey": "test"
]
Alamofire.request("http://name/user_data/\(userName)#someURL.com", headers: headers).responseJSON { response in
print(response.request) // original URL request
print(response.response) // URL response
print(response.data) // server data
print(response.result)
}