Ember JS with remote auth backend (XML response) - javascript

Using Ember-Simple-Auth (custom authenticator) and Ember.$.ajax()
Authentication procedure:
Link to authsite.com/login, and log in there
If OK, external website redirects to mysite.com/someroute?sid=xxx&uid=xxx
I retrieve those parameters
I can get a big XML file with a lot of info by making a request to authsite.com/check?sid=xxx&uid=xxx if user is authenticated. Auth backend constraint: this request must come from the same IP as step 2.
I have a problem making this request because:
Not allowed by Access-Control-Allow-Origin
Not JSONP
Cannot enable CORS
Setting crossDomain: true and xhr credentials to true doesn't work either when doing Ember.$.ajax()
What are my options if I want to do everything in the front-end?

Related

FastAPI is not returning cookies to React frontend

Why doesn't FastAPI return the cookie to my frontend, which is a React app?
Here is my code:
#router.post("/login")
def user_login(response: Response,username :str = Form(),password :str = Form(),db: Session = Depends(get_db)):
user = db.query(models.User).filter(models.User.mobile_number==username).first()
if not user:
raise HTTPException(400, detail='wrong phone number or password')
if not verify_password(password, user.password):
raise HTTPException(400, detail='wrong phone number or password')
access_token = create_access_token(data={"sub": user.mobile_number})
response.set_cookie(key="fakesession", value="fake-cookie-session-value") #here I am set cookie
return {"status":"success"}
When I login from Swagger UI autodocs, I can see the cookie in the response headers using DevTools on Chrome browser. However, when I login from my React app, no cookie is returned. I am using axios to send the request like this:
await axios.post(login_url, formdata)
First, make sure there is no error returned when performing the Axios POST request, and that you get a "status": "success" response with 200 status code.
Second, as you mentioned that you are using React in the frontend—which needs to be listening on a different port from the one used for the FastAPI backend, meaning that you are performing CORS requests—you need to set the withCredentials property to true (by default this is set to false), in order to allow receiving/sending credentials, such as cookies and HTTP authentication headers, from/to other origins. Two servers with same domain and protocol, but different ports, e.g., http://localhost:8000 and http://localhost:3000 are considered different origins (see FastAPI documentation on CORS and this answer, which provides details around cookies in general, as well as solutions for setting cross-domain cookies—which you don't actually need in your case, as the domain is the same for both the backend and the frontend, and hence, setting the cookie as usual would work just fine).
Please note that if you are accessing your React frontend at http://localhost:3000 from your browser, then your Axios requests to FastAPI backend should use the localhost domain in the URL, e.g., axios.post('http://localhost:8000',..., not http://127.0.0.1:8000, as localhost and 127.0.0.1 are two different domains, and hence, the cookie would otherwise fail to be created for localhost domain, as it would be created for 127.0.0.1, i.e., the domain used in axios request (and that would be a case for cross-domain cookies, as described in the linked answer above).
Thus, to accept cookies sent by the server, you need to use withCredentials: true in your Axios request; otherwise, the cookies will be ignored in the response (which is the default behaviour, when withCredentials is set to false; hence, preventing different domains from setting cookies for their own domain). The same withCredentials: true property has to be included in every subsequent request to your API, if you would like the cookie to be sent to the server, so that the user can be authenticated and provided access to protected routes.
Hence, an Axios request that includes credentials should look like this:
await axios.post(url, data, {withCredentials: true}))
The equivalent in a fetch() request (i.e., using Fetch API) is credentials: 'include'. The default value for credentials is same-origin. Using credentials: 'include' will cause the browser to include credentials in both same-origin and cross-origin requests, as well as set any cookies sent back in cross-origin responses. For instance:
fetch('https://example.com', {
credentials: 'include'
});
Note
For either the above to work, you would need to explicitly specify the allowed origins, as described in this answer (behind the scenes, that is setting the Access-Control-Allow-Origin response header). For instance:
origins = ['http://localhost:3000', 'http://127.0.0.1:3000',
'https://localhost:3000', 'https://127.0.0.1:3000']
Using the * wildcard instead would mean that all origins are allowed; however, that would also only allow certain types of communication, excluding everything that involves credentials, such as cookies, authorization headers, etc.
Also, make sure to set allow_credentials=True when using the CORSMiddleware (which sets the Access-Control-Allow-Credentials response header to true).
Example (see here):
app.add_middleware(
CORSMiddleware,
allow_origins=origins,
allow_credentials=True,
allow_methods=["*"],
allow_headers=["*"],
)

Making Get request to Yammer API works using Postman tool but not with Vue-Resource

I am trying to integrate Yammer API in my Vue.JS project, for Http calls I am using Vue-Resource plugin. While making GET Http call to get posts from Yammer it gives me following error -
Response to preflight request doesn't pass access control check: No
'Access-Control-Allow-Origin' header is present on the requested
resource.
I tried postman tool and that gives successful response, but when I try to run the same thing in my Vue.JS project using Vue-Resource plugin it wont work.
The Vue.JS code snippet -
function(){
this.$http.get("https://www.yammer.com/api/v1/messages/my_feed.json").then((data)=>{
console.log(data);
});
In main.vue file i have -
Vue.http.interceptors.push((request, next) => {
request.headers.set('Authorization', 'Bearer my_yammer_token')
request.headers.set('Accept', '*/*')
next()
})
Then I tried the code snippets provided by Postman tool for jquery, that too not working.
jQuery code -
var settings = {
"url": "https://www.yammer.com/api/v1/messages/my_feed.json",
"method": "GET",
"timeout": 0,
"headers": {
"Authorization": "Bearer my_yammer_token",
"Cookie": "yamtrak_id=some_token; _session=some_token"
},
};
$.ajax(settings).done(function (response) {
console.log(response);
});
Though, I found similar questions but nothing worked for me.
I am working this to resolve from last 2 days but getting failed again and again. Please guide/help me.
A browser has higher security requirements than a request in PostMan. In a browser, you are only allowed to make XHR requests to your own current host (combination of domain + port) but not to other remote hosts. To nevertheless make a request to a remote host, you can use the browser built-in CORS. By using this, your browser makes a pre-flight request to the remote host to ask if the current page is allowed to request from that host. This is done via the Access-Control response headers. In your case, this header is probably missing or not allowing your page to access, which is why the request does not go through. Please read further into that topic.
However, in your case, using CORS probably won't be a solution for two reasons: To use CORS, the remote host must present a header which allows every requesting host (*) or your specific one. If you cannot set that setting anywhere on the remote host, it won't work. Second, it is not safe to place your authorization token into client-side JavaScript code. Everybody can just read your JS code and extract the authorization token. For that reason, you usually make the actual API call from the server-side and then pass the data to the client. You can use your own authentication/authorization against your server and then use the static authorization key on the server to request the data from the remote host. In that case, you'll never expose the authorization key to your user. Also, on the server-side, you do not have to deal with CORS as it works just like PostMan or curl as opposed to a browser.

Basic authentication with header - Javascript XMLHttpRequest

I am trying to access Adyen test API that requires basic authentication credentials. https://docs.adyen.com/developers/ecommerce-integration
My credentials work when accessing the API page through browser.
But I get an 401 Unauthorized response when trying to access the API with XMLHttpRequest POST request.
Javascript Code
var url = "https://pal-test.adyen.com/pal/servlet/Payment/v25/authorise";
var username = "ws#Company.CompanyName";
var password = "J}5fJ6+?e6&lh/Zb0>r5y2W5t";
var base64Credentials = btoa(username+":"+password);
var xhttp = new XMLHttpRequest();
xhttp.open("POST", url, true);
xhttp.setRequestHeader("content-type", "application/json");
xhttp.setRequestHeader("Authorization", "Basic " + base64Credentials);
var requestParams = XXXXXXXX;
xhttp.send(requestParams);
Result
That screenshot shows “Request Method: OPTIONS”, which indicates the details displayed are for a CORS preflight OPTIONS request automatically made by your browser—not for your POST.
Your browser doesn’t (and can’t) send the Authorization header when it makes that OPTIONS request, and that causes the preflight to fail, so the browser never moves on to trying your POST.
As long as https://pal-test.adyen.com/pal/servlet/Payment/v25/authorise requires authentication for OPTIONS requests, there’s no way you can make a successful POST to it.
The reason is because what’s happening here is this:
Your code tells your browser it wants to send a request with the Authorization header.
Your browser says, OK, requests with the Authorization header require me to do a CORS preflight OPTIONS to make sure the server allows requests with that header.
Your browser sends the OPTIONS request to the server without the Authorization header—because the whole purpose of the OPTIONS check is to see if it’s OK to send that.
That server sees the OPTIONS request but instead of responding to it in a way that indicates it allows Authorization in requests, it rejects it with a 401 since it lacks that header.
Your browser expects a 200 or 204 response for the CORS preflight but instead gets that 401 response. So your browser stops right there and never tries the POST request from your code.
The PAL is a Payment Authorisation API. You never want to call it from a browser. You only want to expose your username and password to send in payments in your backend code.
In Client-side encryption, the encryption is done in the browser. You then send the encrypted data to your own server. On your server you then create a payment authorization request (of which the encrypted data is one of the elements, along side payment amount, etc).
If you would be able to manage to make this run from your browser, your end solution will allow your shoppers to change amounts, currency's, payment meta data etc from the JavaScript layer. This should never be the case.
The authorization is for that reason part of the "Server side" integration part of documentation: https://docs.adyen.com/developers/ecommerce-integration?ecommerce=ecommerce-integration#serverside
Depending on your server side landscape the CURL implementation in your favorite language differs, but most of the time are easy to find.
Kind regards,
Arnoud

Setting Basic authorization details in request headers

In our application we validate user name/password. Once validation is done, credentials are encoded using base64 and then needs to be set at request header for subsequent rest calls.
Need to set below in request header.
Authorization:Basic AQNLzR69OFTNJE8X
In the response setting as below from the java code,
javax.ws.rs.core.Response.status(200).entity("").header("Authorization:","Basic AQNLzR69OFTNJE8X").build();
And in the javascript tried setting as below,
sessionStorage.setItem('Authorization:', 'Basic AQNLzR69OFTNJE8X');
But in the subsequent rest service calls in the same session can see the header request is not set with authorization. Request to provide some pointers on setting the Authorization in javascript, so that it is retained for the entire session.
I think you misunderstand how authentication works (or should work).
You are supposed to send the Authorization header only once during the authentication. If the authentication is successful, the server sends you back a session cookie and your session is marked as authenticated (server-side).
You never send back the content of the header, and you don't have to send it each request.
1) The Authorization header is not automatically added. But the cookie will be automatically sent.
2) You should not send the credential and return them: for security purposes, you want to transport them the less you can.
3) You don't want to store the credential in the sessionStorage, I don't know if this is a secure place for a password (i doubt it), but here, the password is only encoded in B64, and it's reversable. So it's as well as cleartext (which is bad for a password).
Hopes this helps!

LinkedIn OAuth redirect login returning "No 'Access-Control-Allow-Origin' header is present on the requested resource" error

I'm currently implementing OAuth login with LinkedIn in my React and Play app and am running into a CORS error when trying to redirect to the authorization page in my dev environment:
XMLHttpRequest cannot load https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_i…basicprofile&redirect_uri=http%3A%2F%2Flocalhost%3A9000%2Fusers%2Flinkedin. Redirect from 'https://www.linkedin.com/oauth/v2/authorization?response_type=code&client_i…basicprofile&redirect_uri=http%3A%2F%2Flocalhost%3A9000%2Fusers%2Flinkedin' to 'https://www.linkedin.com/uas/login?session_redirect=%2Foauth%2Fv2%2Flogin-s…' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'null' is therefore not allowed access.
I have the following setup:
Play server running at localhost:9000
React app (created via create-react-app) running at localhost:3000
My JS code calls the /auth/linkedin endpoint which is implemented as follows:
Action { implicit req: RequestHeader =>
val csrfToken = CSRF.getToken.get.value
Redirect(linkedinUrl(oauthConfig.linkedinClientId, csrfToken)).withSession("state" -> csrfToken)
}
I have my Play application set to handle CORS appropriately.
My react app just makes a request to the above endpoint via Axios:
axios.get('/auth/linkedin')
This responds with a 303 with a redirect to the LinkedIn auth page which then gives me the error.
How do I get the CORS policy working correctly in this dev setup? I've tried adding the following to my package.json as the create-react-app documentation recommends:
"proxy": "http://localhost:9000",
And I've also tried setting a request header to "Access-Control-Allow-Origin" : "*" on the redirect in the Play server with no success.
Note that going to localhost:9000/auth/linkedin redirects properly.
https://www.linkedin.com/oauth/v2/authorization responses apparently don’t include the Access-Control-Allow-Origin response header, and because they do not, your browser blocks your frontend JavaScript code from accessing the responses.
There are no changes you can make to your own frontend JavaScript code nor backend config settings that’ll allow your frontend JavaScript code to make requests the way you’re trying directly to https://www.linkedin.com/oauth/v2/authorization and get responses back.
https://developer.mozilla.org/en-US/docs/Web/HTTP/Access_control_CORS explains in more detail but the gist of it is: for CORS, the server the request is being sent to must be configured to send the Access-Control-Allow-Origin response header, nor your own backend server.
2019-05-30 update
The current state of things seems to be that when needing to do LinkedIn authorization, you’ll have to initiate the request from your backend code. There’s no way you can do it from your frontend code, because LinkedIn no longer provides any support for it at all.
LinkedIn did previously provide some support for handling it from frontend code. But the page that documented it, https://developer.linkedin.com/docs/getting-started-js-sdk, now has this:
The JavaScript SDK is not currently supported
And https://engineering.linkedin.com/blog/2018/12/developer-program-updates has this:
Our JavaScript and Mobile Software Development Kits (SDKs) will stop working. Developers will need to migrate to using OAuth 2.0 directly from their apps.
So the remainder of this answer (from 2017-06-13) has now become obsolete. But it’s preserved below for the sake of keeping the history complete.
2017-06-13 details, now obsoleted
Anyway https://developer.linkedin.com/docs/getting-started-js-sdk has official docs that explain how to request authorization for a user cross-origin, which appears to be just this:
<script type="text/javascript" src="//platform.linkedin.com/in.js">
api_key: [API_KEY]
onLoad: [ONLOAD]
authorize: [AUTHORIZE]
lang: [LANG_LOCALE]
IN.User.authorize(callbackFunction, callbackScope);
</script>
And https://developer.linkedin.com/docs/signin-with-linkedin has docs for another auth flow:
<script type="in/Login"></script> <!-- Create the "Sign In with LinkedIn" button-->
<!-- Handle async authentication & retrieve basic member data -->
<script type="text/javascript">
// Setup an event listener to make an API call once auth is complete
function onLinkedInLoad() {
IN.Event.on(IN, "auth", getProfileData);
}
// Handle the successful return from the API call
function onSuccess(data) {
console.log(data);
}
// Handle an error response from the API call
function onError(error) {
console.log(error);
}
// Use the API call wrapper to request the member's basic profile data
function getProfileData() {
IN.API.Raw("/people/~").result(onSuccess).error(onError);
}
</script>
I ran into a similar problem, so let's divide this problem into detailed steps
Hit request to get the code(from frontend)
now send this code to the backend
In the backend, make another call to LinkedIn OAuth API and get the access token
With this access token make 3 separate calls to get the name, profile picture
and email of the user(yes you heard that right you need to make 3 separate calls and also the response JSON format is not very appealing)
Visit this for the detailed step-by-step process, it involves a lot of things. I can just share the process here but for the actual implementation visit this.
https://www.wellhow.online/2021/04/setting-up-linkedin-oauth-and-fixing.html
What could be done is:
window.location.href='http://localhost:9000/auth/linkedin'
The urlEndPoint could be directly to linkedIn's API or a back-end service which makes the call to linkedIn's API.

Categories