Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Spring boot + oauth2 : Full authentication is required to access this resource

I am trying to implement a authentication-server using spring boot, spring cloud security and spring cloud oauth2.

I am getting below error when I try to hit http://localhost:8080/auth/oauth/token from postman

{ "error": "unauthorized", "error_description": "Full authentication is required to access this resource" }

Below is my pom.xml

<?xml version="1.0" encoding="UTF-8"?> 
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> . 
<modelVersion>4.0.0</modelVersion>
<groupId>com.teckink.tp</groupId>
<artifactId>tp-auth-server</artifactId>
<version>0.0.1-SNAPSHOT</version>
<packaging>jar</packaging>

<name>tp-auth-server</name>
<description>Demo project for Spring Boot</description>

<parent>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-parent</artifactId>
    <version>2.0.0.RELEASE</version>
    <relativePath/> <!-- lookup parent from repository -->
</parent>

<properties>
    <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
    <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
    <java.version>1.8</java.version>
    <spring-cloud.version>Finchley.M9</spring-cloud.version>
</properties>

<dependencies>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-oauth2</artifactId>
    </dependency>
    <dependency>
        <groupId>org.springframework.cloud</groupId>
        <artifactId>spring-cloud-starter-security</artifactId>
    </dependency>

    <dependency>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-test</artifactId>
        <scope>test</scope>
    </dependency>
</dependencies>

<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframework.cloud</groupId>
            <artifactId>spring-cloud-dependencies</artifactId>
            <version>${spring-cloud.version}</version>
            <type>pom</type>
            <scope>import</scope>
        </dependency>
    </dependencies>
</dependencyManagement>

<build>
    <plugins>
        <plugin>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-maven-plugin</artifactId>
        </plugin>
    </plugins>
</build>

<repositories>
    <repository>
        <id>spring-milestones</id>
        <name>Spring Milestones</name>
        <url>https://repo.spring.io/milestone</url>
        <snapshots>
            <enabled>false</enabled>
        </snapshots>
    </repository>
</repositories>
</project>

Starter(Main) class:

package com.teckink.tp.authserver;

import java.util.HashMap;
import java.util.Map;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.security.core.authority.AuthorityUtils;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableAuthorizationServer;
import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
import org.springframework.security.oauth2.provider.OAuth2Authentication;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@SpringBootApplication
@RestController
@EnableResourceServer
@EnableAuthorizationServer
public class App {
    @RequestMapping(value = { "/user" }, produces = "application/json")
    public Map<String, Object> user(OAuth2Authentication user) {
        Map<String, Object> userInfo = new HashMap<>();
        userInfo.put("user", user.getUserAuthentication().getPrincipal());
        userInfo.put("authorities", AuthorityUtils.authorityListToSet(user.getUserAuthentication().getAuthorities()));
        return userInfo;
    }


    public static void main(String[] args) {
        SpringApplication.run(App.class, args);
    }

}

OAuth2Config class which defines client and its secret:

package com.teckink.tp.authserver.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.oauth2.config.annotation.configurers.ClientDetailsServiceConfigurer;
import org.springframework.security.oauth2.config.annotation.web.configuration.AuthorizationServerConfigurerAdapter;
import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;

@Configuration
public class OAuth2Config extends AuthorizationServerConfigurerAdapter {

    @Autowired
    private AuthenticationManager authenticationManager;

    @Autowired
    private UserDetailsService userDetailsService;

    @Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("eagleeye")
                .secret("thisissecret")
                .authorizedGrantTypes("refresh_token", "password", "client_credentials")
                .scopes("webclient", "mobileclient");
    }

    @Override
    public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
      endpoints
        .authenticationManager(authenticationManager)
        .userDetailsService(userDetailsService);
    }
}

WebSecurityConfigurer class which defines in-memory user, password and roles:

package com.teckink.tp.authserver.security;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.userdetails.UserDetailsService;

@Configuration
public class WebSecurityConfigurer extends WebSecurityConfigurerAdapter {
        @Override
    @Bean
    public AuthenticationManager authenticationManagerBean() throws Exception {
        return super.authenticationManagerBean();
    }

   @Override
    @Bean
    public UserDetailsService userDetailsServiceBean() throws Exception {
        return super.userDetailsServiceBean();
    }


    @Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth
                .inMemoryAuthentication()
                .withUser("john.carnell").password("password1").roles("USER")
                .and()
                .withUser("william.woodward").password("password2").roles("USER", "ADMIN");
    }
}

I am calling Rest API from POSTMAN as below- Request with authentication screen show as below : enter image description here

Request with form-data as below enter image description here

like image 715
Prithvipal Singh Avatar asked Apr 01 '18 18:04

Prithvipal Singh


People also ask

What is OAuth 2.0 and how it works in spring boot?

OAuth2 is an authorization framework that enables the application Web Security to access the resources from the client. To build an OAuth2 application, we need to focus on the Grant Type (Authorization code), Client ID and Client secret.


3 Answers

The following changes fixed this issue for me when reading Spring Microservices in Action by John Carnell.

Changed the following property

application.yml

server:
  contextPath: /auth

To

server:
  servlet:
    context-path: /auth

And I've also added {noop} to every secret/password and now it works!

WebSecurityConfigurer.java

@Override
    protected void configure(AuthenticationManagerBuilder auth) throws Exception {
        auth.inMemoryAuthentication()
                .withUser("john.carnell").password("{noop}password1").roles("USER")
                .and()
                .withUser("william.woodward").password("{noop}password2").roles("USER", "ADMIN");
    }

OAuth2Config.java

@Override
    public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
        clients.inMemory()
                .withClient("eagleeye")
                .secret("{noop}thisissecret")
                .authorizedGrantTypes("refresh_token", "password", "client_credentials")
                .scopes("webclient", "mobileclient");
    }

I am using Spring Cloud Hoxton SR11

like image 196
Marina Avatar answered Oct 30 '22 19:10

Marina


I have same issue. the url is wrong, change to http://localhost:8080/oauth/token then it's OK. I got this sample from book which is giving the wrong url. just remove the "/auth"

like image 45
dqshll Avatar answered Oct 30 '22 20:10

dqshll


In your sample app when you run, you would have got the following exception

java.lang.IllegalArgumentException: There is no PasswordEncoder mapped for the id "null"

In spring-security-core:5.0, the default PasswordEncoder is built as a DelegatingPasswordEncoder. Hence when you store the users in memory, you are providing the passwords in plain text and then when you are trying to retrieve the encoder from the DelegatingPasswordEncoder to validate the password it can't find one.

More details in this link Password Encoding

To address this , for a production implementation you should activate an instance of BCryptPasswordEncoder

For development, you can try out the following changes so that you can override password encoding by adding {noop} to the password value. This will treat the password by activating the NoOpPasswordEncoder instead of the default DelegatingPasswordEncoder and will treat your password as plain text.

OAuth2Config class

clients.inMemory()
            .withClient("eagleeye")
            .secret("{noop}thisissecret")
            .authorizedGrantTypes("refresh_token", "password", "client_credentials")
            .scopes("webclient", "mobileclient");

WebSecurityConfigurer class

 auth
                .inMemoryAuthentication()
                .withUser("john.carnell"). password("{noop}password1").roles("USER")
                .and()
                .withUser("william.woodward").password("{noop}password2").roles("USER", "ADMIN");

Now when you try from Postman, you would be able to generate token enter image description here

EDIT

Github project with a working demo here

like image 27
CGS Avatar answered Oct 30 '22 21:10

CGS