<div class="CONTAINER">
<div class="CARD">
<h3>Login</h3>
<input id="signInEmailInput" class="input" placeholder="Email">
<input id="signInPasswordInput" class="input" placeholder="Password">
<button class="signInButton" onclick="login_user()">Login</button>
</div>
</div>
// Cool color palette
$color1: #5D737E; $color2: #64B6AC; $color3: #D0F4DE; $color4: #1B4332;
// Warm color palette
$color5: #FFB447; $color6: #FF3E4D; $color7: #FF1E56; $color8: #FFBD69;
// Animating Backgrounds
@keyframes fade1 {
0% { background-color: $color1} 25% { background-color: $color2} 50% { background-color: $color3} 75% { background-color: $color4} 100% { background-color: $color1}
}
@keyframes fade2 {
0% { background-color: $color5} 25% { background-color: $color6} 50% { background-color: $color7} 75% { background-color: $color8} 100% { background-color: $color5}
}
.login-container {
display: flex;
justify-content: center;
align-items: center;
color: black;
.card {
width: 300px;
padding: 20px;
border-radius: 10px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
animation: fade1 5s ease infinite;
}
.card2 {
width: 300px;
padding: 20px;
border-radius: 10px;
box-shadow: 0px 0px 10px rgba(0, 0, 0, 0.1);
animation: fade2 5s ease infinite;
}
h3 {
margin-bottom: 20px;
}
.input {
width: 100%;
padding: 10px;
margin-bottom: 10px;
}
}
Name | ID | Age |
---|
this html fragment represents our table which we fill with data using our function.
function userDbRequest() {
// prepare HTML result container for new output
const resultContainer = document.getElementById("result");
// set options for cross origin header request
const options = {
method: 'GET', // *GET, POST, PUT, DELETE, etc.
mode: 'cors', // no-cors, *cors, same-origin
cache: 'default', // *default, no-cache, reload, force-cache, only-if-cached
credentials: 'include', // include, *same-origin, omit
headers: {
'Content-Type': 'application/json',
},
};
fetch("http://localhost:8085/api/person/", options)
.then(response => {
if (response.status !== 200) {
const errorMsg = 'Database response error: ' + response.status;
console.log(errorMsg);
const tr = document.createElement("tr");
const td = document.createElement("td");
td.innerHTML = errorMsg;
tr.appendChild(td);
resultContainer.appendChild(tr);
return;
}
// valid response will contain json data
response.json().then(data => {
console.log(data);
for (const row of data) {
// tr and td build out for each row
const tr = document.createElement("tr");
const name = document.createElement("td");
const id = document.createElement("td");
const age = document.createElement("td");
// data is specific to the API
name.innerHTML = row.name;
id.innerHTML = row.email;
age.innerHTML = row.age;
// this build td's into tr
tr.appendChild(name);
tr.appendChild(id);
tr.appendChild(age);
// add HTML to container
resultContainer.appendChild(tr);
}
})
})
// catch fetch errors (ie ACCESS to server blocked)
.catch(err => {
console.error(err);
const tr = document.createElement("tr");
const td = document.createElement("td");
td.innerHTML = err + ": " + url;
tr.appendChild(td);
resultContainer.appendChild(tr);
});
}
function login_user() {
var myHeaders = new Headers();
myHeaders.append("Content-Type", "application/json");
// STEP ONE: COLLECT USER INPUT
var raw = JSON.stringify({
"email": document.getElementById("signInEmailInput").value,
"password": document.getElementById("signInPasswordInput").value
// For quick testing
//"email": "toby@gmail.com",
//"password": "123Toby!"
});
console.log(raw);
var requestOptions = {
method: 'POST',
headers: myHeaders,
credentials: 'include',
body: raw,
redirect: 'follow'
};
// STEP TWO: SEND REQUEST TO BACKEND AND GET JWT COOKIE
fetch("http://localhost:8085/authenticate", requestOptions)
.then(response => {
if (!response.ok) {
const errorMsg = 'Login error: ' + response.status;
console.log(errorMsg);
switch (response.status) {
case 401:
alert("Incorrect username or password");
break;
case 403:
alert("Access forbidden. You do not have permission to access this resource.");
break;
case 404:
alert("User not found. Please check your credentials.");
break;
// Add more cases for other status codes as needed
default:
alert("Login failed. Please try again later.");
}
return Promise.reject('Login failed');
}
return response.text()
})
.then(result => {
console.log(result);
window.location.href = "http://127.0.0.1:4100/Login-Lesson/account";
})
.catch(error => console.error('Error during login:', error));
}
this function sends an authentication request to backend, which then redirects to our database page.
the authentication request in question is located in spring_portfolio/mvc/jwt/JwtApiController.java
FOR REFERENCE:
@PostMapping("/authenticate")
public ResponseEntity<?> createAuthenticationToken(@RequestBody Person authenticationRequest) throws Exception {
authenticate(authenticationRequest.getEmail(), authenticationRequest.getPassword());
final UserDetails userDetails = personDetailsService
.loadUserByUsername(authenticationRequest.getEmail());
final String token = jwtTokenUtil.generateToken(userDetails);
final ResponseCookie tokenCookie = ResponseCookie.from("jwt", token)
.httpOnly(true)
.secure(true)
.path("/")
.maxAge(3600)
.sameSite("None; Secure")
// .domain("example.com") // Set to backend domain
.build();
return ResponseEntity.ok().header(HttpHeaders.SET_COOKIE, tokenCookie.toString()).build();
}
private void authenticate(String username, String password) throws Exception {
try {
authenticationManager.authenticate(new UsernamePasswordAuthenticationToken(username, password));
} catch (DisabledException e) {
throw new Exception("USER_DISABLED", e);
} catch (BadCredentialsException e) {
throw new Exception("INVALID_CREDENTIALS", e);
} catch (Exception e) {
throw new Exception(e);
}
}
this is the authentication method we are calling to, which provides us with the cookie we need for access to our database.
function fetchUserData() {
var requestOptions = {
method: 'GET',
mode: 'cors',
cache: 'default',
credentials: 'include',
};
fetch("http://localhost:8085/api/person/jwt", requestOptions)
.then(response => {
if (!response.ok) {
const errorMsg = 'Login error: ' + response.status;
console.log(errorMsg);
switch (response.status) {
case 401:
alert("Please log into or make an account");
window.location.href = "http://127.0.0.1:4100/Login-Lesson/loginSignup";
break;
case 403:
alert("Access forbidden. You do not have permission to access this resource.");
break;
case 404:
alert("User not found. Please check your credentials.");
break;
// Add more cases for other status codes as needed
default:
alert("Login failed. Please try again later.");
}
return Promise.reject('Login failed');
}
return response.json();
// Success!!!
})
.then(data => {
// Display user data above the table
const userDataContainer = document.getElementById("userData");
userDataContainer.innerHTML = `
<img src="/Login-Lesson/images/defaultUser.png" width="250" height="250">
<h1><strong>${data.name}</strong></h1>
<p>Email: ${data.email}</p>
<p>Age: ${data.age}</p>
<p>ID: ${data.id}</p>
<button onclick="signOut()">Sign Out</button>
`;
console.log(data);
})
.catch(error => console.log('error', error));
}
this function resides in our database display on our frontend, and does 2 things
@GetMapping("/jwt")
@PreAuthorize("isAuthenticated()") // Restrict access to authenticated users
public ResponseEntity<Person> getAuthenticatedPersonData() {
String username = SecurityContextHolder.getContext().getAuthentication().getName();
Person person = repository.findByEmail(username); // Retrieve data for the authenticated user
return new ResponseEntity<>(person, HttpStatus.OK);
}
function signup_user() {
var requestOptions = {
method: 'POST',
mode: 'cors',
cache: 'no-cache'
};
// Collect user input
let fetchName = document.getElementById("signUpNameInput").value;
let fetchEmail = document.getElementById("signUpEmailInput").value;
let fetchPassword = document.getElementById("signUpPasswordInput").value;
let fetchDob = document.getElementById("signUpDobInput").value;
// Posting in backend only works if user input is sent as query parameters
let requestURL = `http://localhost:8085/api/person/post?email=${fetchEmail}&password=${fetchPassword}&name=${fetchName}&dob=${fetchDob}`;
console.log(requestURL)
fetch(requestURL, requestOptions)
.then(response => {
if (!response.ok) {
return response.text().then(errorMsg => {
alert('Error: ' + errorMsg);
});
}
// Success!!!
alert("Signup Complete");
// Redirect to Database location
location.reload();
})
.catch(error => {
alert('An unexpected error occurred: ' + error.message);
});
}
.authorizeHttpRequests(auth -> auth
.requestMatchers("/authenticate").permitAll()
.requestMatchers("/mvc/person/update/**", "/mvc/person/delete/**").authenticated()
// .requestMatchers("/api/person/post/**", "/api/person/delete/**").authenticated()
// Removed so anyone without a cookie can post
.requestMatchers("/api/person/delete/**").authenticated()
.requestMatchers("/**").permitAll()
)
@PostMapping( "/post")
// @RequestParam is why user input needs to be a parameter
public ResponseEntity<Object> postPerson(@RequestParam("email") String email,
@RequestParam("password") String password,
@RequestParam("name") String name,
@RequestParam("dob") String dobString) {
Date dob;
// dob handling
try {
dob = new SimpleDateFormat("MM-dd-yyyy").parse(dobString);
} catch (Exception e) {
return new ResponseEntity<>(dobString +" error; try MM-dd-yyyy", HttpStatus.BAD_REQUEST);
}
// A person object WITHOUT ID will create a new record with default roles as student
Person person = new Person(email, password, name, dob);
personDetailsService.save(person);
return new ResponseEntity<>(email +" is created successfully", HttpStatus.CREATED);
}