Spring security new Authorization server (0.3.1) - part 2
Tags: java, security, oauth2
Published: 2022-09-25 14:28:10

In the previous part we've set up the minimum authorization server with in memory users and clients. In this part we will change the implementation slightly and make users not be stored in memory but instead in database.

Let's start with the entity

```java
@Entity
@Getter
@Setter
@NoArgsConstructor
public class AppUser implements UserDetails {

  @Id
  private String id;
  private String username;
  private String password;
  @ManyToMany(fetch = FetchType.EAGER)
  @JoinTable(name = "user_authority",
      joinColumns = @JoinColumn(name = "user_id", referencedColumnName = "id"),
      inverseJoinColumns = @JoinColumn(name = "authority_id", referencedColumnName = "id"))
  private Collection<Authority> authorities;
  private Boolean isAccountExpired = false;
  private Boolean isAccountLocked = false;
  private Boolean isCredentialsExpired = false;
  private Boolean isEnabled = true;

  public AppUser(String username, String password,
      Collection<Authority> authorities) {
    this.id = UUID.randomUUID().toString();
    this.username = username;
    this.password = password;
    this.authorities = authorities;
  }

  @Override
  public Collection<? extends GrantedAuthority> getAuthorities() {
    return this.authorities;
  }

  @Override
  public String getPassword() {
    return this.password;
  }

  @Override
  public String getUsername() {
    return this.username;
  }

  @Override
  public boolean isAccountNonExpired() {
    return !this.isAccountExpired;
  }

  @Override
  public boolean isAccountNonLocked() {
    return !this.isAccountLocked;
  }

  @Override
  public boolean isCredentialsNonExpired() {
    return !this.isCredentialsExpired;
  }

  @Override
  public boolean isEnabled() {
    return this.isEnabled;
  }
}
```

We will use the `ProviderManager` with `DaoAuthenticationProvider` from Spring security, hence we implement the `UserDetails` in the entity.

You can also notice the `@ManyToMany` relationship to authorities.
Let's see the `Authority` entity:

```java
@Entity
@NoArgsConstructor
@Getter
@Setter
public class Authority implements GrantedAuthority {

  @Id
  private String id;
  private String name;

  public Authority(String name) {
    this.id = UUID.randomUUID().toString();
    this.name = name;
  }


  @Override
  public String getAuthority() {
    return this.name;
  }
}
```
There's not much here, authority only has a name and an id. We had to implement the `GrantedAuthority` because if you remember in the `UserDetails` authorities is a `Collection<? extends GrantedAuthority>`. Hibernate will also create a join table based on the @JoinTable annotation in the `AppUser` entity.

The configuration for the default security config will have to change from the previous part, let's see what it looks like now:

```java
@Configuration
@RequiredArgsConstructor
public class SecurityConfig {

  private final UserService userService;

  @Bean
  public SecurityFilterChain defaultSecurityFilterChain(HttpSecurity http)
      throws Exception {

    http
        .authorizeRequests()
        .antMatchers("/users/**").permitAll()
        .anyRequest().authenticated()
        .and()
        .csrf().ignoringAntMatchers("/users/**")
        .and()
        .formLogin(Customizer.withDefaults());

    return http.build();
  }

  @Bean
  public AuthenticationManager authenticationManagerBean() throws Exception {
    var provider = new DaoAuthenticationProvider();
    provider.setUserDetailsService(userService);
    provider.setPasswordEncoder(NoOpPasswordEncoder.getInstance()); //temporary
    return new ProviderManager(provider);
  }

  @Bean
  public UserDetailsService userDetailsService() {
    return this.userService;
  }

}
```

First, I allowed all requests to /users/** pattern, because I need a way to create a user. I only have an endpoint to create a new user, but in a real case scenario you probably would not allow all requests like this. After that I also need to disable csrf since I imagine you will want to call this endpoint from an spa. If you may want to create a register page as part of the spring app like the login page then you should leave csrf.

Next we expose an `AuthenticationManager` bean where we return a new `ProviderManager` and pass only a `DaoAuthenticationProvider` as it's provider. Before that we also instruct `DaoAuthenticationProvider` to use our implementation of `UserDetailsService`, which we injected before, and here is the implementation:

```java
@Service
@RequiredArgsConstructor
public class UserService implements UserDetailsService {

  private final AppUserRepository appUserRepository;

  @Override
  public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException {
    return this.appUserRepository.findByUsername(username)
        .orElseThrow(() -> new UsernameNotFoundException("User not found"));
  }

  public UserResponse save(CreateUserRequest request) {
    var user = new AppUser(request.getUsername(), request.getPassword(), List.of());
    this.appUserRepository.save(user);
    return new UserResponse(user);
  }
}
```

Also you would probably not use the `NoOpPasswordEncoder`, but for simplicity sake I used it here. You can simply use any other `PasswordEncoder` there, you will likely also want to expose it as bean, and then when creating the user, before saving you would encode the password.

Now let's test the changes. Same as in the previous part, I will open the following link:
http://localhost:8080/oauth2/authorize?response_type=code&client_id=client&scope=openid&redirect_uri=http://spring.io&code_challenge=YHRCg0i58poWtvPg_xiSHFBqCahjxCqTyUbhYTAk5ME&code_challenge_method=S256

You should create your own code_challenge and verifier and replace them before opening the link.

I will be redirected to the /login page, but now I can't login yet because I don't have any users in the database. First let's create a user:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/tnk2mxdwa4iiput2ocdy.png)

**You should not return a password in the response like this, it's just for demonstration purposes.**

Now let's use the username and password to login, you should get redirected to 'spring.io?code=...', copy the code and call the token endpoint:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/8fifvewidaa8emb4o2vf.png)

Again, make sure you put your own code_verifier and code you got from the previous redirect.

Now we have users in the database, but our clients are still stored in memory. In the next part we will do the same thing but for the clients.