my problem is very "simple". I don't know how to setup my OAUTH2 auth server to accept username/password and returns me token.
If I use:
curl curl:password#localhost:8081/oauth/token\?grant_type=client_credentials
it returns me the token but the problem is that it registers user "curl" in the DB so... not so good...
If I use:
http://www.example.com:8081/oauth/authorize?client_id=web&response_type=token
it prompts username and password dialog, I enter them and then it asks me I "Do you authorize 'web' to access your protected resources?
scope.read: Approve Deny"
Can I combine those two and just create simple request which will return me the token? I want to use it for angularjs frontend using RESTful WS in Spring Boot and Jersey.
Should I use this scheme
https://github.com/spring-projects/spring-security-oauth/blob/master/spring-security-oauth2/src/test/resources/schema.sql
use this config -> clients.jdbc(dataSource);
How to setup one user for that scheme? just basic login with username and password.
OauthConfiguration
#Configuration
#EnableAuthorizationServer
public class OAuthConfiguration extends AuthorizationServerConfigurerAdapter
{
#Autowired
private DataSource dataSource;
#Bean
public TokenStore tokenStore()
{
return new JdbcTokenStore(dataSource);
}
#Override
public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception
{
endpoints.tokenStore(tokenStore());
}
#Override
public void configure(ClientDetailsServiceConfigurer clients) throws Exception
{
// #formatter:off
clients.inMemory()
.withClient("curl")
.authorities("USER")
.resourceIds("reee")
.scopes("read", "write")
.authorizedGrantTypes("client_credentials")
.secret("password")
.and()
.withClient("web")
.redirectUris("http://github.com/techdev-solutions/")
.resourceIds("reee")
.scopes("read")
.authorizedGrantTypes("implicit");
// #formatter:on
}
}
SecurityConfiguration
#Configuration
#EnableWebSecurity
public class SecurityConfiguration extends WebSecurityConfigurerAdapter
{
#Autowired
private DataSource dataSource;
#Override
protected void configure(HttpSecurity http) throws Exception
{
http.authorizeRequests().antMatchers("/**").authenticated()
.and().httpBasic().realmName("OAuth Server");
}
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception
{
PasswordEncoder encoder = new BCryptPasswordEncoder();
auth.userDetailsService(userDetailsService()).passwordEncoder(encoder);
auth.jdbcAuthentication().dataSource(dataSource);
}
#Bean
public UserDetailsService userDetailsService()
{
return new CustomUserDetailsService(dataSource);
}
}
i think we have similar issues as i have opened a q & a what are the java* configuration for oauth2 to return token after authentication
you can avoid the username and password box by adding username and password in the header of the request http://localhost:8081/oauth/authorize?client_id=web&response_type=token and in your client configuration for web add clients.inMemory().autoApprove(true) however server will respond with the redirect url. My problem is that I don't want it to redirect I just want the token in the response..
Related
I am trying to implement Signup with Github in a sample project, where front-end code is made up of ReactJS and Backend API is made up of Spring Boot.
I am not using Server Template Engines such as ThymeLeaf or Mustache, but only creating RESTful APIs.
Below is my Spring Security Configuration.
#RequiredArgsConstructor
#EnableWebSecurity
public class SecurityConfig extends WebSecurityConfigurerAdapter {
private final GithubOAuth2UserService githubOAuth2UserService;
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.httpBasic().disable()
.csrf().disable()
.sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS)
.and()
.authorizeRequests()
.antMatchers("/v1/health-check")
.permitAll()
.and()
.logout()
.logoutSuccessUrl("/")
.and()
.oauth2Login()
.userInfoEndpoint()
.userService(githubOAuth2UserService);
}
}
Below is my code for GithubOAuth2UserService
#RequiredArgsConstructor
#Service
public class GithubOAuth2UserService implements OAuth2UserService<OAuth2UserRequest, OAuth2User> {
private final UserRepository userRepository;
#Override
public OAuth2User loadUser(OAuth2UserRequest userRequest) throws OAuth2AuthenticationException {
OAuth2UserService<OAuth2UserRequest, OAuth2User> delegate = new DefaultOAuth2UserService();
OAuth2User oAuth2User = delegate.loadUser(userRequest);
String registrationId = userRequest.getClientRegistration().getRegistrationId();
System.out.println(registrationId);
String userNameAttributeName = userRequest.getClientRegistration()
.getProviderDetails().getUserInfoEndpoint()
.getUserNameAttributeName();
System.out.println(userNameAttributeName);
OAuthAttributes attributes = OAuthAttributes.ofGithub(userNameAttributeName, oAuth2User.getAttributes());
User user = saveUser(attributes);
return new DefaultOAuth2User(
Collections.singleton(new SimpleGrantedAuthority(user.getRole().name())),
attributes.getAttributes(),
attributes.getNameAttributeKey()
);
}
#Transactional
protected User saveUser(OAuthAttributes attributes) {
System.out.println("SAVE USER!");
if(userRepository.findByEmail(attributes.getEmail()).isPresent()) {
throw new UserEmailConflictException();
} else {
User user = attributes.toEntity();
return userRepository.save(user);
}
}
}
Finally, OAuthAttributes is as below.
#Getter
public class OAuthAttributes {
private Map<String, Object> attributes;
private String nameAttributeKey;
private String name;
private String email;
#Builder
public OAuthAttributes(Map<String, Object> attributes, String nameAttributeKey, String name, String email) {
this.attributes = attributes;
this.nameAttributeKey = nameAttributeKey;
this.name = name;
this.email = email;
}
public static OAuthAttributes ofGithub(String userNameAttributeName, Map<String, Object> attributes) {
return OAuthAttributes.builder()
.name((String) attributes.get("name"))
.email((String) attributes.get("email"))
.attributes(attributes)
.nameAttributeKey(userNameAttributeName)
.build();
}
public User toEntity() {
return User.builder()
.name(name)
.email(email)
.build();
}
}
Via PostMan, I can see the API result as below, but my ReactJS code doesn't show anything, and just refreshes the page.
On ReactJS, it calls this API like below.
// OnClick function
const result = await axios.get("http://localhost:8080/oauth2/authorization/github");
console.log(result);
UPDATE
I am getting CORS policy violation, and I configured this using a configuration class below. Is this related to SecurityConfig class ?
#Configuration
#EnableWebMvc
public class CORSConfig implements WebMvcConfigurer {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*").allowedMethods("*");
}
}
Thank you so much in advance. If any code needs to be shown, I will happily edit this post.
I have problem with Spring Oauth2 again. I know this topic is not easy to suggest sth or check the codes because we have too much configuration.
My project has 3 different servers, Authentication server, resource server and front-end server. I want to put register.html to user's registration in front-end project(under Angularjs files) but when I make request to the related url (http://localhost:7080/app/#register) its redirecting to the login page (http://localhost:9080/auth-service/login) only for a second i can see my register.html content but after that its going to login page.
The question is, where should i put this register.html, it should be under front-end project or authentication server ?
My authentication server and front-end server codes are;
#Configuration
public class AuthServerSecurityConfiguration extends WebSecurityConfigurerAdapter {
#Autowired
private AuthenticationManager authenticationManager;
#Override
protected void configure(AuthenticationManagerBuilder auth)
throws Exception {
auth.parentAuthenticationManager(authenticationManager);
auth.authenticationProvider(userAuthProviderService());
}
private CsrfMatcher csrfRequestMatcher = new CsrfMatcher();
#Override
protected void configure(final HttpSecurity http) throws Exception {
/*http.csrf().disable();*/
http.csrf().requireCsrfProtectionMatcher(csrfRequestMatcher);
http
.formLogin().loginPage("/login").defaultSuccessUrl("/")
/*.failureUrl("")*/.successHandler(new AuthSuccessHandler()).permitAll()
.and()
.requestMatchers().antMatchers("/login", "/oauth/authorize", "/oauth/confirm_access","/register")
.and()
.authorizeRequests().anyRequest().authenticated();
}
#Bean
public UserAuthProviderService userAuthProviderService(){
return new UserAuthProviderService();
}
private class CsrfMatcher implements RequestMatcher {
#Override
public boolean matches(HttpServletRequest request) {
return false;
}
}
}
#Configuration
#EnableAutoConfiguration
#RestController
#EnableZuulProxy
#EnableOAuth2Sso
#EnableOAuth2Client
public class UIServiceMain {
public static void main(String[] args) {
SpringApplication.run(UIServiceMain.class, args);
}
#Configuration
protected static class SecurityConfiguration extends OAuth2SsoConfigurerAdapter {
#Override
public void configure(HttpSecurity http) throws Exception {
http.logout().and()
.antMatcher("/**").authorizeRequests()
.antMatchers("/index.html", "/home.html", "/", "/login","/register.html").permitAll().anyRequest()
.authenticated().and().csrf().disable();
http.headers().frameOptions().disable(); //FOR EMBED MAP
}
//unused
private Filter csrfHeaderFilter() {
return new OncePerRequestFilter() {
#Override
protected void doFilterInternal(HttpServletRequest request,
HttpServletResponse response, FilterChain filterChain)
throws ServletException, IOException {
CsrfToken csrf = (CsrfToken) request.getAttribute(CsrfToken.class
.getName());
if (csrf != null) {
Cookie cookie = WebUtils.getCookie(request, "XSRF-TOKEN");
String token = csrf.getToken();
if (cookie == null || token != null
&& !token.equals(cookie.getValue())) {
cookie = new Cookie("XSRF-TOKEN", token);
cookie.setPath("/");
response.addCookie(cookie);
}
}
filterChain.doFilter(request, response);
}
};
}
//unused
private CsrfTokenRepository csrfTokenRepository() {
HttpSessionCsrfTokenRepository repository = new HttpSessionCsrfTokenRepository();
repository.setHeaderName("X-XSRF-TOKEN");
return repository;
}
}
}
in your UI server try to create websecurity with /register.hml enabled, something like this
#Configuration
public class WebSecurityConfiguration extends WebSecurityConfigurerAdapter{
#Override
protected void configure(HttpSecurity http) throws Exception {
http
.requestMatchers()
.antMatchers("/register.html")
.and()
.authorizeRequests()
.anyRequest().authenticated();
}
}
edit:
or maybe in your current configuration remove .antMatcher("/**").authorizeRequests() and add and() .authorizeRequests().anyRequest().authenticated()
so finally it could be something like this:
#Override
public void configure(HttpSecurity http) throws Exception {
http.logout().and()
.antMatchers("/index.html", "/home.html", "/", "/login","/register.html").permitAll().anyRequest()
.authenticated()
.and().csrf().disable();
http.headers().frameOptions().disable() //FOR EMBED MAP
.and()
.authorizeRequests()
.anyRequest().authenticated();
}
Couple of things:
I can't think of a good reason not to put your *.html anywhere other than front end server.
Also, in general, you should permit access to your static UI components publically, like #bilak mentioned:
.antMatchers("/index.html", "/home.html", "/", "/login","/register.html").permitAll()
If you are able to see register.html page at all (assuming unauthenticated user) then it is public already
Perhaps, there is a webservice call on register.html's load event that is behind Spring security that is triggering the auth flow.
I am trying to successfully authenticate a user with Spring Security using AngularJS on the front end.
Basically what is supposed to happen is:
1) A new user should be able to fill out the registration form with a unique username and password
2) Upon submit, Angular POSTs the user/pass combination to the URL specified by spring security.
3) Spring Security verifies the account and logs the user in.
4) The user's session begins and is shown as logged in on the front end.
The hangup is occurring in step 2. The information is not successfully posted to the spring login URL. My failure handler is tripped (in the code below) and the login process is halted.
POST http://localhost:8080/libroomreserve/login 401 (Unauthorized)
Here is my Spring Security Config:
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private DataSource dataSource;
#Autowired
private AuthenticationFailure authFailure;
#Autowired
private AuthenticationSuccess authSuccess;
#Autowired
private EntryPointUnauthorizedHandler unauthorizedHandler;
#Autowired
private UserDetailServiceImpl userDetails;
#Override
protected void configure(HttpSecurity http) throws Exception{
http
.csrf().disable()
.exceptionHandling()
.authenticationEntryPoint(unauthorizedHandler)
.and()
.formLogin()
.successHandler(authSuccess) //sets status to 200 OK
.failureHandler(authFailure) //sets status to 401 Unauthorized
.and()
.authorizeRequests()
.antMatchers("/**")
.permitAll();
}
#Autowired
public void configureGlobal(AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetails);
}
Failure Handler:
#Component
public class AuthenticationFailure extends SimpleUrlAuthenticationFailureHandler{
#Override
public void onAuthenticationFailure(HttpServletRequest request, HttpServletResponse response, AuthenticationException exception) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_UNAUTHORIZED);
}
}
Success Handler:
#Component
public class AuthenticationSuccess extends SimpleUrlAuthenticationSuccessHandler{
#Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse response, Authentication authentication) throws IOException, ServletException {
response.setStatus(HttpServletResponse.SC_OK);
}
}
AuthenticationEntryPoint:
#Component
public class EntryPointUnauthorizedHandler implements AuthenticationEntryPoint{
#Override
public void commence(HttpServletRequest hsr, HttpServletResponse hsr1, AuthenticationException ae) throws IOException, ServletException {
hsr1.sendError(HttpServletResponse.SC_UNAUTHORIZED, "Access Denied.");
}
}
On the AngularJS side, this is my service:
.factory('sessionService', function($http, $base64){
var session = {};
session.login = function(data){
return $http.post("/libroomreserve/login", "username=" + data.userName + "&password" + data.password,
{
headers: {
'Content-Type' : 'application/x-www-form-urlencoded'
}
})
//.then() is a "PROMISE" which is executed after initial return function is performed
.then(function(){
console.log("Logged in the user!");
localStorage.setItem("session", {});
}, function(){
console.log("Error logging in the user...");
});
};
session.logout = function(){
localStorage.removeItem("session");
console.log("User has been logged out.");
};
session.isLoggedIn = function(){
return localStorage.getItem("session") !== null;
};
return session;
})
For reference, I am following the tutorial here by Chris Henkel. I can't find any discrepancies between his code and mine.
As an aside, the newly-registered user is being logged into the database so the credentials are available for authentication.
I am using:
- Facebook-JavaScript-SDK (for login)
- Angular-JS (front end)
- Spring-Security (server security layer)
- Spring MVC (server MVC)
User's can log-into my web app by
(A) either using Facebook login (implemented in javascript FB SDK) OR
(B) via normal username/password form.
In case of (B)
i do a Form Post with fields:
j_username
j_password
to /j_spring_security_check (Spring Security)
This works fine.
But in case of (A), after the user has logged in via FB (in front-end),
What do i need to do to have Spring Security create the Session for the user?
All i have is facebook user's email with me at this point, and Server is unaware of this login in the front-end.
After successful login with Face Book,you should make AJAX request to your back-end API and make database entry about the Face book authentication for the user.
1)You need to implement UserDetailsService from Spring-Security
#Component
public class CustomUserDetailsService implements UserDetailsService {
#Override
public UserDetails loadUserByUsername(String userEmail) throws UsernameNotFoundException {
// check for Facebook authentication(DB Entry)
// fetch user details and roles from db using e-mail
SimpleGrantedAuthority authority = new SimpleGrantedAuthority(roles);
UserDetails usd = new User(userId, userId, grantedAuths);
return usd;
}
}
2) Implement custom authentication provider
#Component("customAuthenticationProvider")
public class CustomAuthenticationProvider implements AuthenticationProvider {
#Autowired
private CustomUserDetailsService customUserDetailsService;
#Override
public Authentication authenticate(Authentication authentication) throws AuthenticationException {
String userId = authentication.getName();
String password = authentication.getCredentials().toString();
UserDetails userFromDB = customUserDetailsService.loadUserByUsername(userId);
if (userFromDB != null) {
List<GrantedAuthority> grantedAuths = new ArrayList<GrantedAuthority>();
for (GrantedAuthority auth : userFromDB.getAuthorities()) {
grantedAuths.add(auth);
}
if (grantedAuths.size() != 0) {
System.out.println("user:" + userId + " got auth from custom auth provider");
return new UsernamePasswordAuthenticationToken(userFromDB, password, grantedAuths);
} else {
throw new DisabledException("No roles assigned");
}
}
throw new DisabledException("User or password incorrect");
}
#Override
public boolean supports(Class<?> authentication) {
return authentication.equals(UsernamePasswordAuthenticationToken.class);
}
}
3) Login controller
#Controller
public class LoginController {
#Autowired
private CustomAuthenticationProvider customAuthenticationProvider;
#Autowired
private CustomUserDetailsService customUserDetailsService;
#RequestMapping(value = "/login", )
public ResponseEntity<Object> fbLogin(#RequestParam String userEmail, HttpServletRequest request) {
UserDetails userDetails=customUserDetailsService.loadUserByUsername(userid);
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(userDetails, userid);
token.setDetails(new WebAuthenticationDetails(request));
Authentication authentication = this.customAuthenticationProvider.authenticate(token);
}
}
4) Spring Security Config
#Configuration
#EnableWebSecurity
public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
#Autowired
private CustomAuthenticationProvider authProvider;
#Override
protected void configure(AuthenticationManagerBuilder auth) throws Exception {
auth.authenticationProvider(authProvider);
}
}
I am trying to enable both JDBA and Active Directory Authentication , i made great progress but currently i am stuck as userDetailsService is trying to compare the password in LdapUserDetails which does not exist . When checking the log i see it is able to query the user and authenticate and get the roles correctly.
I know i should use bindService or so , but i couldn't find till now how to do that.
Below is what i did .
in WebSecurityConfigurerAdapter
#Autowired
public void configureGlobal(UserDetailsService userDetailsService,UserLdapRepositoryUserDetailsService userLdapRepositoryUserDetailsService,AuthenticationManagerBuilder auth) throws Exception {
auth
.userDetailsService(userDetailsService).
and()
.userDetailsService(userLdapRepositoryUserDetailsService);
}
For LDAP Configuration
#Bean
public BaseLdapPathContextSource contextSource() {
DefaultSpringSecurityContextSource contextSource = new DefaultSpringSecurityContextSource("ldap://XXXXX:389/dc=XXXXXX,dc=co");
//contextSource.setUserDn("CN=Ali Shahbour,OU=Users,DC=XXXXXXX,DC=co");
contextSource.setUserDn("XXXXXX");
contextSource.setPassword("XXXXXX");
return contextSource;
}
#Bean
#Autowired
public LdapUserSearch userSearch(BaseLdapPathContextSource contextSource) {
FilterBasedLdapUserSearch userSearch = new FilterBasedLdapUserSearch("", "(uid={0})", contextSource);
return userSearch;
}
#Bean
#Autowired
public LdapAuthoritiesPopulator authoritiesPopulator(BaseLdapPathContextSource contextSource) {
DefaultLdapAuthoritiesPopulator authoritiesPopulator = new DefaultLdapAuthoritiesPopulator(contextSource, "OU=CDRMonitor");
authoritiesPopulator.setGroupSearchFilter("(member={0})");
//authoritiesPopulator.setRolePrefix("ROLE");
authoritiesPopulator.setSearchSubtree(true);
//authoritiesPopulator.setConvertToUpperCase(true);
return authoritiesPopulator;
}
As for the LdapUserDetailsService
#Service("userLdapRepositoryUserDetailsService")
public class UserLdapRepositoryUserDetailsService extends LdapUserDetailsService {
private final UserRepository userRepository;
#Autowired
public UserLdapRepositoryUserDetailsService(LdapUserSearch userSearch,
LdapAuthoritiesPopulator authoritiesPopulator,UserRepository userRepository) {
super(userSearch, authoritiesPopulator);
this.userRepository = userRepository;
}
#Override
public UserDetails loadUserByUsername(String username)
throws UsernameNotFoundException {
UserDetails userDetails = super.loadUserByUsername(username);
//User user = userRepository.findByEmail(username);
User user = new User();
return new LdapUserRepositoryUserDetails(user, userDetails);
}
#Override
public void setUserDetailsMapper(UserDetailsContextMapper userDetailsMapper) {
super.setUserDetailsMapper(userDetailsMapper);
}
private final static class LdapUserRepositoryUserDetails extends User implements LdapUserDetails {
private final LdapUserDetailsImpl ldapUserDetailsImpl;
private LdapUserRepositoryUserDetails(User user,UserDetails userDetails) {
super(user);
ldapUserDetailsImpl = (LdapUserDetailsImpl) userDetails;
}
private static final long serialVersionUID = 5639683223516504866L;
#Override
public Collection<? extends GrantedAuthority> getAuthorities() {
return ldapUserDetailsImpl.getAuthorities();
}
#Override
public String getUsername() {
// TODO Auto-generated method stub
return ldapUserDetailsImpl.getUsername();
}
#Override
public boolean isAccountNonExpired() {
return ldapUserDetailsImpl.isAccountNonExpired();
}
#Override
public boolean isAccountNonLocked() {
return ldapUserDetailsImpl.isAccountNonLocked();
}
#Override
public boolean isCredentialsNonExpired() {
return ldapUserDetailsImpl.isCredentialsNonExpired();
}
#Override
public boolean isEnabled() {
return ldapUserDetailsImpl.isEnabled();
}
#Override
public String getDn() {
return ldapUserDetailsImpl.getDn();
}
}
}
LDAP and SQL in an authentication context are in general not used together,
because LDAP BIND authentication sends the password to the LDAP server instead of retrieving a hash value of the password.
The intended procedure of LDAP Authentication is as follows:
Usually a spring security UsernamePassword filter is used to pick up the credentials and is active by default. e.g. if you us a login form, when submitting the form, this filter picks up the credentials.
Next an LDAP Authentication Provider performs a login (LDAPBindAuthenticator) against the LDAP server (LDAP ContextSource) to verify the credentials.
If the login is successful, the LDAP Authentication Provider searches the LDAP for a user entry. This can be customised by providing a 'usersearch' spring bean.
If the user entry is found, the LDAP Authority mapper will map attributes of the user entry to groups/roles in spring security. By default this are all OU attributes
Lastly a new authentication object is made with the username and retrieved groups from the LDAP.
How to integrate LDAP Authentication using Spring XML is explained here.
http://docs.spring.io/spring-security/site/docs/3.1.x/reference/ldap.html