react axios request is empty in loadUserByUsername spring security - reactjs

When I send axios put to login, My back-end (Spring Boot) used UserDetails get empty login (""). I thought that, my variables in axios is incorrect. Even I set hardcoded data to check whether the collected data from the form is correct. Unfortunately, I'm still sending an empty string.
const handleSubmit = (event) => {
const axios = require('axios').default;
event.preventDefault();
axios.defaults.withCredentials = true
axios({
method: 'post',
url: 'http://localhost:8080/login',
data: {
'accountLogin': accountLogin,
'passwordAccount': passwordAccount
}
})
.then(function (response) {
console.log(response);
console.log('Good!')
})
.catch(function (error) {
console.log(error);
console.log('error :<')
});
result username in spring UserDetails
[result in my browser]
Spring security configure:
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.cors().and()
.csrf().disable()
.authorizeRequests()
.antMatchers("/", "/login", "/register", "/swagger-ui**").permitAll()
.anyRequest().authenticated()
.and()
.formLogin()
.loginPage("http://localhost:3000")
.defaultSuccessUrl("/index", true)
.and()
.sessionManagement()
.sessionCreationPolicy(SessionCreationPolicy.IF_REQUIRED)
.and()
.logout().permitAll()
.invalidateHttpSession(true);
}

there are several faults in your code and i highly suggest you read the chapter on formLogin in the spring security documentation
You have configured this:
.formLogin()
formLogin means that you are going to use the built in login functionality in spring security. This is a form login, meaning it takes FORM parameters. You are sending your login parameters as JSON which is completely different.
sending form data with fetch
const formData = new FormData();
formData.append('username', accountLogin);
formData.append('password', accountPassword);
axios({
url: "http://localhost:8080/login",
method: "POST",
data: formData
});
Spring security formLogin takes in a usernameand a password per default as form parameters. Not json. And it will create a default login address for you to post the data to at http://localhost:8080/login
.loginPage("http://localhost:3000")
Should be removed. Spring will provide a custom built in login page that will get served if you hit a protected endpoint when you are unauthorized. If you wish to serve a different endpoint, you set this here and make sure that a pake is served there. For instance:
.loginPage("/myCustomLogin")
And then it is up to you to make sure that a login page is served from here otherwise you will get a 404 from spring security.
Depending on how you are packaging your application, if you have a node server for your frontend and a spring server for the backend, then you dont need to use loginPage you just post form data to /login. If you on the otherhand server the loginpage from the spring server, you set loginPage to some value, build an endpoint there, package the html files in the spring server, and write code that will serve the HTML page when you hit that endpoint.

This is a better way of sending post requests with Axios. try this, if it didn't work I guess the problem is with your back-end code.
axios.post('http://localhost:8080/login', {
accountLogin: accountLogin,
passwordAccount: passwordAccount
})
.then(function (response) {
console.log(response);
})
.catch(function (error) {
console.log(error);
});

Related

Client does not receive cookies from server, postman does

There is a server, which serves my client react app at root path. So when I make any request to server from POSTMAN, to login for example, cookies are attached perfect. But when I make request from my client using AXIOS and withCredentials field as well, cookies ain't attached, nevertheless the request is sent good, but no cookies received. I don't think there is any reason to search issues in server code, because postman works with it perfect. In case, there is no CORS errors: server provides client app. I get nice response from the server, with no cookies. Postman gets them.
axios request in react app:
export const login = createAsyncThunk(
'auth/login',
async (credentials: ILogin) => {
// todo: making a request to server
const response = await axios({
url: '/api' + '/auth' + '/login',
method: 'POST',
data: credentials,
withCredentials: true,
headers: {
'Content-Type': 'application/json'
},
});
console.log(response)
}
)
Client doesn't receive cookies, neither on localhost nor deployed app.
As you see, only place where cookies are shown it's network section in devtools, but everything else, including server acts like my second request hadn't any cookie, because in this case, server would answer like: agh, already logged in
P.S: i'm using http

how to request in react + spring security?

I've been troubled for days by the application of spring security...
#Override
protected void configure(HttpSecurity http) throws Exception {
http.csrf().disable();
http.addFilter(corsFilter);
http.authorizeRequests()
.antMatchers("/user/**").authenticated()
.antMatchers("/manager/**").access("hasRole('ROLE_ADMIN') or hasRole('ROLE_MANAGER')")
.anyRequest().permitAll();
http.formLogin()
.loginProcessingUrl("/login-process")
.defaultSuccessUrl("/", false);
}
My front-end is React using 3000 port and not in spring source folder. so i think i can't use spring default login form. so i thought it would work if i sent a "POST" request to loginProcessingUrl.
fetch('http://localhost:8081/login-process', {
credentials: 'include',
headers: {
'Content-Type': 'application/json'
},
method:"POST",
body:{"username":"user","password":"d4ddeb71-c76b-43f3-a0af-2f3c0b36bbec"}
})
.then((json) => console.log(json));
but i got "http://localhost:8081/login?error" url as response.
username and password is spring gave to me. and if i enter "http://localhost:8081/login"(default login form) using chrome browser and login with these, it works well. so i don't think username or password is wrong.
i've seen https://www.baeldung.com/spring-security-login-react, but I think it's different from my case where the front end is separated.
what's the problem? should i approach it differently?

How to fetch data from a REST API by using an API-Token

I'm trying to fetch data from the Jira Rest API in my React application by using the Axios library for http requests. An API token is necessary, in order to access data via the Jira API. I generated an API token in my Jira account settings, but I can't figure out, how to include it in my http request to gain access.
This is the endpoint provided by the Jira documentation for getting an issue from the Jira board:
curl -u admin:admin http://localhost:8080/jira/rest/api/2/issue/TEST-10 | python -mjson.tool
This is the React state hook for setting the data to the fetched data:
const [jiraTicket, setJiraTicket] = useState([]);
This is the fetch function for the API request (${} will be filled with user input):
function getJiraTicket() {
axios.get(`${username}:${apiToken}#Content-Type:application/json/https:/${jiraSiteName}.atlassian.net/rest/api/2/issue/${projectKey}-${ticketId}`)
.then((res) => {
const data = res.data;
setJiraTicket(data);
})
}
The button inside the react component return should invoke the fetch function:
return(
<Container>
<Button onClick{getJiraTicket()}>Fetch Jira Ticket</Button>
</Container>
);
This is the error I'm currently getting, because the authorization is not working the way I did it
(I replaced the provided username, API token etc. for this example):
GET http://localhost:3000/username:apitoken#https:/sitename.atlassian.net/rest/api/2/issue/projectkey-ticketid 404 (not found)
Edit:
My current approach:
function getJiraTicket() {
axios.get(`${userName}:${apiToken}#https://${siteName}.atlassian.net/rest/api/2/issue/${projectId}-${ticketId}`,{
auth: {
username: userName,
password: apiToken,
},
withCredentials: true
})
.then((res) => {
const data = res.data;
console.log(data);
setJiraTicket(data);
})
.catch(err => {
// This error means: The request was made and the server responded with a status code
if(err.res) {
console.log(err.res.data);
console.log(err.res.status);
console.log(err.res.headers);
console.log("request was made and server responded with status");
// The request was made but no response was received
} else if (err.request) {
console.log(err.request);
console.log("request was made, but no response was received");
// Something happened in setting up the request that triggered an error
} else {
console.log("Error", err.message);
console.log("request is note set up correctly");
}
console.log(err.config);
})
Current error, which I defined accordingly to the axios doc: "request was made, but no response was received"
Endpoint that works well in Postman (Basic auth is provided in Postman):
https://sitename.atlassian.net/rest/api/2/issue/projectid-ticketid
Update: CORS access isn't allowed, when an application tries to access the Jira API endpoints directly. This restriction takes place in order to prevent random authenticated requests to the specific Jira site, because the access is based on session based authentication. However the API endpoints can be accessed, if OAuth 2.0 is used instead of Basic auth, because the application will redirect the user to the Jira auth itself via this link:
https://auth.atlassian.com/authorize? audience=api.atlassian.com&
client_id=YOUR_CLIENT_ID&
scope=REQUESTED_SCOPE_ONE%20REQUESTED_SCOPE_TWO&
redirect_uri=https://YOUR_APP_CALLBACK_URL&
state=YOUR_USER_BOUND_VALUE& response_type=code& prompt=consent
Source: https://developer.atlassian.com/cloud/jira/platform/oauth-2-3lo-apps/#known-issues
Axios uses a headers config for get/post so you should not include them in your URL. Here is a general example of how you should construct the URL and apply headers:
let axiosUrl = `https://${jiraSiteName}.atlassian.net/rest/api/2/issue/${projectKey}-${ticketId}`
axios({
baseURL: axiosUrl,
method: 'get',
headers: {
"Content-Type": "application/json",
"Access-Control-Allow-Origin", "*"
},
//timeout: 2000,
auth: {
username: userName,
password: apiToken,
}
})
.then((res) => {
setJiraTicket(res.data);
})
.catch(function (error) {
console.log(error);
});

reactjs make https (not http) requests with axios

I'm trying to make https requests to the server using axios. Most of the tutorials regarding axios specify how to make http requests.
I make the requests whenever users login. Here is my current request:
axios.post('/api/login/authentication', {
email: email,
password: password
})
.then(response => {
this.props.history.push('/MainPage')
})
.catch(error => {
console.log(error)
})
Can anyone help me convert this to an https request?
All URLs have two parts
Domain - http://yourdomain.com
Path - /path-to-your-endpoint
1. Use default domain
In axios, if you specify just the path, it will use the domain in the address bar by default.
For example, the code below will make a call to whatever domain is in your address bar and append this path to it. If the domain is http, your api request will be a http call and if the domain is https, the api request will be a https call. Usually localhost is http and you will be making http calls in localhost.
axios.post('/api/login/authentication', {
2. Specify full URL with domain
On the other hand, you can pass full URL to axios request and you will be making https calls by default.
axios.post('https://yourdomain.com/api/login/authentication', {
2. Use axios baseURL option
You can also set baseURL in axios
axios({
method: 'post',
baseURL: 'https://yourdomain.com/api/',
url: '/login/authentication',
data: {
email: email,
password: password
}
}).then(response => {
this.props.history.push('/MainPage')
})
.catch(error => {
console.log(error)
});

Basic Authentication of spring with Restangular

I am using Restangular with Spring's oauth security and in the client side i am using Restangular for login request.
Code in OAuth2ServerConfiguration:
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
clients
.inMemory()
.withClient("clientapp")
.authorizedGrantTypes("password", "refresh_token")
.authorities("USER")
.scopes("read", "write")
.secret("abc");
}
Login with postman needs these configurations:
1-Set Authorization as "Basic Auth".
2-Set username,password as {"username":"clientapp","password":"abc"}//credentials to access server side
3-In request body through "x-www-form-urlencoded" three parameters are sent.
{"username":"abc#gmail.com","password":"abc123","grant_type":"password"}//credentials to login which are checked from database.
This will do a successful login.but i cannot understand how to use these configurations in Angular JS Restangular call.
currently m trying with this.
In Config:
RestangularProvider.withConfig(function (RestangularConfigurer) {
return RestangularConfigurer.setDefaultHeaders({ "Authorization": "Basic Y2xpZW50YXBwOkxNUw==",
"username":"clientapp",
"password":"abc",
"Content-type": "application/x-www-form-urlencoded; charset=utf-8"
});
In Controller:
Restangualar.all("oauth/login").post({username;$scope.user.username,
password:"$scope.user.password","grant_type":"password"}).then(function(){
console.log(res);
});
But I am getting this error:
error:"unauthorized",error_description:"Full authentication is required to access this resource"
in browser.
Note:This resource is not secured.
Any Solution???
Update: I forgot to added a main information that my frontend with angular is running independently on localhost(through xampp) while spring login backend is on localhost:8080..
Error in network tab:
2-
public void configure(HttpSecurity http) throws Exception {
// #formatter:off
http
.authorizeRequests()
.anyRequest().permitAll()
//.antMatchers("/users").permitAll()
.antMatchers(HttpMethod.OPTIONS, "/oauth/token").permitAll()
.and().csrf().disable();
}
3-
endpoints
.tokenStore(this.tokenStore)
.authenticationManager(this.authenticationManager)
.userDetailsService(userDetailsService)
.addInterceptor(new HandlerInterceptorAdapter() {
public boolean preHandle(HttpServletRequest hsr, HttpServletResponse rs, Object o,FilterChain chain) throws Exception {
rs.setHeader("Access-Control-Allow-Origin", "*");
rs.setHeader("Access-Control-Allow-Methods", "GET,OPTIONS,POST");
// rs.setHeader("Access-Control-Max-Age", "7200");
rs.setHeader("Access-Control-Allow-Headers", "Origin, X- Requested-With, Content-Type, Accept, Authorization");
HttpServletRequest httpServletRequest = (HttpServletRequest) hsr;
if (httpServletRequest.getMethod().equalsIgnoreCase("OPTIONS")) {
chain.doFilter(hsr, rs);
} else {
// In case of HTTP OPTIONS method, just return the response
return true;
}
return false;
}
});
You can use Restangular custom post. See documentation.
Example:
Restangular.service("/oauth/login").one().customPOST(
{},
'',
{
// Params...
grant_type: 'password',
client_id: 'clientapp',
client_secret: 'abc',
username: 'abc#gmail.com',
password: 'abc123',
scope: 'read, write'
},
{
// headers...
}).then(
function (response) {
// Manage successfull response
},
function () {
// Manage error response
}
);
Hope it helps
UPDATED:
It seems to be a CORS problem, lots of answers already for it, but in your case using XAMPP you will need to configure your apache server:
https://enable-cors.org/server_apache.html.
PREVIOUS ANSWER BEFORE UPDATE:
The advantage of using restangular is the ability to manage resources in a more semantic way and the ability to get nested resources. All these advantages don't really apply for a call just to retrieve a token from an oauth2 provider.
I would recommend to forget about using restangular for this specific call (you still can use it for everything else in your application) and convert this call to a simple $http.post.
$http.post('oauth/login',
{ username;$scope.user.username,
password:"$scope.user.password",
"grant_type":"password"
},
{
headers: { "Authorization": "Basic Y2xpZW50YXBwOkxNUw==",
"username":"clientapp",
"password":"abc",
"Content-type": "application/x-www-form-urlencoded; charset=utf-8"
}
})
.then(function(response) {
Restangular.setDefaultHeaders({
"Authorization": "Bearer " + response.token
});
});
So, you just use $http.post, and on its response set the default headers in angular to use the retrieved token.
Cheers,

Resources