---
# System prepended metadata

title: Authenticating the User - Part 2 (2233)
tags: [' spring', ' react', authentication]

---

---
tags: authentication, react, spring
---
# Authenticating the User - Part 2 (2233)

We will now work on completing the sign-in process.

## Backend Updates
We will need to make a few more updates to our Spring Boot code. There are 2 ways a user can authenitcate to the API: (1) using a Firebase and (2) using the authorization token we give them.

#### Response Wrapper
Update the response class to have an additional parameter `HttpHeaders headers`

```java=
@Data
@RequiredArgsConstructor
@AllArgsConstructor
public class ResponseWrapper {
    private @NonNull int statusCode;
    private @NonNull String name;
    private @NonNull Object payload;
    private @Nullable HttpHeaders headers = null;

    public ResponseEntity getResponse()
    {
        return ResponseEntity.status(statusCode).headers(headers).body(Collections.singletonMap(name, payload));
    }
}
```

## User Service

We will need a few additional functions to for our log in to work. We need to be able fetch a user by their the uid and an easy way to update the last login value.

```java=
    public User getUserByUid(String uid) throws ExecutionException, InterruptedException {
        User user = null;

        Query query = db.collection("User")
                .whereEqualTo("uid", uid);
        ApiFuture<QuerySnapshot> future = query.get();
        List<QueryDocumentSnapshot> docs = future.get().getDocuments();

        if(docs.size() == 1)
            user = docs.get(0).toObject(User.class);

        return user;
    }

  public void updateLastLogin(String id){
        DocumentReference docRef = db.collection("User").document(id);
        ApiFuture<WriteResult> writeResult = docRef.update("lastLogin", FieldValue.serverTimestamp());

    }
```


### application.yml

Add to the `exposed-headers`
1. X-Auth-Token
2. Expires

This allow us to pass these values back in the hearder on our CORS call.

### FirebaseAuthenticationFilter

We will update the authentication filter to work with both the Firebase toke and our own JWT token.

```java=
 @Override
protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    try {
        String authToken = extractAuthenticationTokenFromRequest(request);

        if (StringUtils.hasText(authToken)) {

            FirebaseToken decodedToken = FirebaseAuth.getInstance().verifyIdToken(authToken);
            String uid = decodedToken.getUid();
            Collection<GrantedAuthority> authorities = new ArrayList<>();
            authorities.add(new SimpleGrantedAuthority("USER"));
            Authentication authentication = new UsernamePasswordAuthenticationToken(uid, null, authorities);
            SecurityContextHolder.getContext().setAuthentication(authentication);
        }
        else
        {
            authToken = extractAuthorizationTokenFromRequest(request);

            if (StringUtils.hasText(authToken)) {
                Claims claims = JwtUtil.getClaimsFromToken(authToken);
                String uid = claims.getSubject();
                Collection<GrantedAuthority> authorities = new ArrayList<>();
                authorities.add(new SimpleGrantedAuthority("USER"));
                Authentication authentication = new UsernamePasswordAuthenticationToken(uid, null, authorities);
                SecurityContextHolder.getContext().setAuthentication(authentication);
            }
        }

    } catch (AuthenticationException e) {
        failureHandler.onAuthenticationFailure(request, response, e);
        return;
    } catch (FirebaseAuthException e) {
        throw new RuntimeException(e);
    }
    catch (ExpiredJwtException | UnsupportedJwtException | MalformedJwtException | SignatureException | IllegalArgumentException e){
        //TODO: Handle exception
        //throw new RuntimeException(e);
    }
    filterChain.doFilter(request, response);
}

private String extractAuthenticationTokenFromRequest(HttpServletRequest request) {

    String authToken = request.getHeader("Authorization");
    if (StringUtils.hasText(authToken) && authToken.startsWith("Bearer ")) {
        return authToken.substring(7);
    }
    return null;
}

private String extractAuthorizationTokenFromRequest(HttpServletRequest request) {
    return request.getHeader("X-Auth-Token");
}
```

### AuthenticationController

Now we will update the authentication controller.

```java=
@RestController
@RequestMapping("/api")
public class AuthenticationController {

    private final FirebaseAuth firebaseAuth = FirebaseAuth.getInstance();
    private final AuthenticationManager authenticationManager;

    @Value("${response.status}")
    private int statusCode;
    @Value("${response.name}")
    private String name;
    private Object payload;
    private ResponseWrapper response;
    private static final String CLASS_NAME = "Authentication";


    private final Log logger = LogFactory.getLog(this.getClass());

    public AuthenticationController(AuthenticationManager authenticationManager) {

        this.authenticationManager = authenticationManager;
    }

    @PostMapping("/register")
    public String register(@RequestBody RegistrationRequest registrationRequest) throws FirebaseAuthException {
        UserRecord.CreateRequest createRequest = new UserRecord.CreateRequest()
                .setEmail(registrationRequest.getEmail())
                .setPassword(registrationRequest.getPassword())
                .setDisplayName(registrationRequest.getDisplayName());
        UserRecord userRecord = firebaseAuth.createUser(createRequest);
        return "User created with UID: " + userRecord.getUid();
    }

    @PostMapping("/login")
    public ResponseEntity login(@RequestBody LoginRequest loginRequest) throws FirebaseAuthException {
        String token = "";
        HttpHeaders headers = new HttpHeaders();

        try {
            UserRecord userRecord = firebaseAuth.getUserByEmail(loginRequest.getEmail());
            //logger.info(userRecord.getUid());

            UserDetails userDetails = new FirebaseUserDetails(userRecord);
            //logger.info(userDetails);
            token = JwtUtil.generateToken(userDetails);
            logger.info(token);
            UserService service = new UserService();
            User user = service.getUserByUid(userRecord.getUid());
            payload = user;
            service.updateLastLogin(user.getUserId() );

            statusCode = 200;
            name = "user";
            headers.add("X-Auth-Token", token);
            Instant now = Instant.now();
            Instant expiryDate = now.plus(1, ChronoUnit.HOURS);
            headers.add("Expires", String.valueOf(expiryDate.toEpochMilli()));

        } catch (ExecutionException | InterruptedException e) {

            payload = new ErrorMessage("Error signing in", CLASS_NAME, e.toString());
        }

        response = new ResponseWrapper(statusCode, name, payload, headers);

        return response.getResponse();
    }


    @GetMapping("/logout")
    public String logout() {
        SecurityContextHolder.getContext().setAuthentication(null);
        return "Logged out successfully";
    }
}
```

## Front End Updates

Let's start by adding the SignIn page.

## SignIn

This is a stateless component.

```jsx=
const context = useContext(AuthContext);
const emailRef = useRef("");
const passwordRef = useRef("");

let navigate = useNavigate();

useEffect(()=>{
    window.document.body.classList.add("text-center");

},[])

async function handleSubmit(event){

    event.preventDefault();

    await context.login(emailRef.current.value, passwordRef.current.value).then(()=>{
        if(context.isLoggedIn && context.currentUser != null)
        {
            navigate("/");
        }
    })



}
return (
    <main className="form-signin w-25 m-auto">
        <form onSubmit={handleSubmit}>
            <img className="mb-4" src={logo} alt="" width="324" />
            <h1 className="h3 mb-3 fw-normal">Please sign in</h1>

            <div className="form-floating">
                <input type="email" className="form-control" id="floatingInput" placeholder="name@example.com" ref={emailRef}/>
                    <label htmlFor="floatingInput">Email address</label>
            </div>
            <div className="form-floating">
                <input type="password" className="form-control" id="floatingPassword" placeholder="Password" ref={passwordRef}/>
                    <label htmlFor="floatingPassword">Password</label>
            </div>


            <button className="mt-3 w-100 btn btn-lg btn-primary" type="submit">Sign in</button>
            <p className="mt-5 mb-3 text-body-secondary"> &copy; 2017–2023</p>
        </form>
    </main>
);
```

Add this page to the router `App.js` as `/signin`. This is **not** a private route.


### AuthContext

Now we need to update the AuthContext to work with our login. First we need to install the firebase library.

```bash
npm i firebase --save
```

#### Create Web App on Firebase

Our login is handled on the front end. To be able to do this, we will need to create a Firebase web application.

![](https://i.imgur.com/6cl71PO.gif)

Add your firebaseConfig to the AuthContext as a variable.

```jsx=
const AuthContext = createContext({
    currentUser: {},
    setCurrentUser: ()=>{},
    isLoggedIn: false,
    login: () => {},
    logout: () => {},
    register: () =>{}
});

const AuthProvider = ({ children }) => {
    const [isLoggedIn, setIsLoggedIn] = useState(localStorage.getItem("authorize") ? true : false);
    const [currentUser, setUser] = useState(localStorage.getItem("currentUser"));

    const fakeUser = {
        "userId": "WgpGQl7XD8WGgetGxvMF",
        "uid": "v8qSFHyaxgZhAAsCuADn4h5l7G32",
        "username": "moneypenny",
        "email": "pstone@example.com",
        "firstName": "Penny",
        "middleName": "",
        "lastName": "Stone",
        "intro": "Bazinga!",
        "profile": "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus placerat orci eget tortor posuere sagittis. Nullam ullamcorper finibus sapien at imperdiet. Etiam et libero turpis. Vestibulum id lectus efficitur, interdum nibh vitae, semper nisi. Curabitur tellus ante, rutrum sed ullamcorper non, egestas ac massa. Vivamus accumsan tempor sapien nec consectetur. Phasellus vulputate mauris nec metus suscipit, ut vestibulum dui viverra.",
        "mobile": "",
        "registeredAt": {
            "seconds": 1674840783,
            "nanos": 287000000
        },
        "lastLogin": null
    }

    // Your web app's Firebase configuration
    const firebaseConfig = {
        apiKey: "",
        authDomain: "",
        projectId: "",
        storageBucket: "",
        messagingSenderId: "",
        appId: ""
    };

    // Initialize Firebase
    initializeApp(firebaseConfig);
    const auth = getAuth();


    const setCurrentUser = (user)=>{
        setUser(user);
    }
    const login = async (email, password) => {

        await signInWithEmailAndPassword(auth,email, password).then(
            async cred => {
                let user = cred.user;
                let res = await user.getIdTokenResult(false);
                let token = res.token;
             
                let headers = {"Authorization": "Bearer " + token}
                await axios.post('http://localhost:8080/api/login', {
                    "email": email, "password": ""
                }, {
                    headers: headers,
                    context: document.body
                }).then((response) => {
                    setCurrentUser(response.data.user)
                    setIsLoggedIn(true);
                    localStorage.setItem("authorize", response.headers.get("X-Auth-Token"));
                    localStorage.setItem("currentUser", JSON.stringify(response.data.user));
                }).catch(e => {
                    console.log(e)
                })


        }).catch(e => console.log(e))

 


    };

    const register = async (username, password) => {
        try {
            await createUserWithEmailAndPassword(username, password);
            setIsLoggedIn(true);
        } catch (error) {
            console.log(error);
        }
    };

    const logout = async () => {
        try {
            await signOut();
            setIsLoggedIn(false);
            // Do something else here, like clear the token from local storage
        } catch (error) {
            console.log(error);
        }
    };

    return (
        <AuthContext.Provider
            value={{
                currentUser,
                isLoggedIn,
                setCurrentUser,
                login,
                register,
                logout,
            }}
        >
            {children}
        </AuthContext.Provider>
    );
};

const AuthConsumer = AuthContext.Consumer;
export { AuthContext, AuthProvider, AuthConsumer };
```

### Use Authorization Token

Finally, now that you have the `X-Auth-Token`, you need to add this a header on each axios call that requires authentication/authorization.

#### Example - MyPosts

Add the header object to the constructor
```javascript!
this.headers = {
            "X-Auth-Token": localStorage.getItem("authorize")
        }
```
 Then update the axios calls:
 
 ```javascript!
await axios
            .get(`http://localhost:8080/api/post/user/${this.userId}`, {headers: this.headers})
```

```javascript!
 await axios
            .put(`http://localhost:8080/api/post/${postId}`, {
                published: newValue
            }, {headers: this.headers})
```

```javascript!
 await axios
            .delete(`http://localhost:8080/api/post/${postId}`, {headers: this.headers})
```

Go through all the files and update your axios calls.