├── .gitignore ├── .travis.yml ├── README.md ├── auth-server ├── pom.xml └── src │ ├── main │ ├── java │ │ └── st │ │ │ └── malike │ │ │ └── auth │ │ │ └── server │ │ │ ├── AuthServerMain.java │ │ │ ├── config │ │ │ ├── ClientAuthenticationToken.java │ │ │ ├── CustomMongoDBConvertor.java │ │ │ └── UserAuthenticationToken.java │ │ │ ├── exception │ │ │ ├── AuthenticationException.java │ │ │ ├── InvalidClientException.java │ │ │ └── InvalidUserException.java │ │ │ ├── model │ │ │ ├── ClientDetail.java │ │ │ ├── OAuth2AuthenticationAccessToken.java │ │ │ ├── OAuth2AuthenticationRefreshToken.java │ │ │ └── User.java │ │ │ ├── repository │ │ │ ├── ClientDetailRepository.java │ │ │ ├── OAuth2AccessTokenRepository.java │ │ │ ├── OAuth2RefreshTokenRepository.java │ │ │ └── UserRepository.java │ │ │ ├── service │ │ │ ├── UserService.java │ │ │ └── security │ │ │ │ ├── ClientDetailService.java │ │ │ │ ├── TokenStoreService.java │ │ │ │ ├── UserAuthConfigService.java │ │ │ │ └── UserAuthProviderService.java │ │ │ └── util │ │ │ └── CORSFilter.java │ └── resources │ │ ├── application.properties │ │ └── spring-security-oauth2.xml │ └── test │ └── java │ └── st │ └── malike │ └── auth │ └── server │ └── service │ └── UserServiceTest.java ├── client ├── pom.xml └── src │ └── main │ ├── java │ └── st │ │ └── malike │ │ └── auth │ │ └── client │ │ ├── AuthClientMain.java │ │ ├── http │ │ └── DemoController.java │ │ └── security │ │ ├── ResourceServerConfig.java │ │ └── SecurityConfig.java │ └── resources │ └── application.properties ├── hello_no_token.png ├── hello_token.png ├── oauth_login.png └── pom.xml /.gitignore: -------------------------------------------------------------------------------- 1 | /client/target/ 2 | /auth-server/target/ -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - oraclejdk8 4 | services: 5 | - mongodb 6 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | [![Build Status](https://travis-ci.org/malike/sso-auth.svg?branch=master)](https://travis-ci.org/malike/sso-auth) 2 | 3 | # sso-auth 4 | 5 | From [Using Spring Security OAuth 2.0 and MongoDB For Single Sign Authentication Server](http://malike.github.io/Spring-Security-OAuth2/) I explained how I chose OAuth 2.0 and MongoDB to build a Single Sign On Auth server that can be used in a microservice architecture. 6 | 7 | *1.* First step. Run sso-auth-client application. And try accessing [http://localhost:8081/hello](http://localhost:8081/hello). Without 8 | passing any headers it returns this 9 | 10 | ![No Security Token](hello_no_token.png "sso-auth-client") 11 | 12 | *2.* Second step. Run sso-auth-server. Lets use the ***password*** **grant_type** to get an access token. 13 | [http://localhost:8080/oauth/token?grant_type=password&username=user@awesome.com&password=cant_hack_this&client_id=sso-auth-client&client_secret=mySecret](http://localhost:8080/oauth/token?grant_type=password&username=user@awesome.com&password=cant_hack_this&client_id=sso-auth-client&client_secret=mySecret) 14 | 15 | ![Get Access Token](oauth_login.png "sso-auth-server") 16 | 17 | *3.* Final step. Lets access [http://localhost:8081/hello](http://localhost:8081/hello) with our access token. 18 | And we are in. 19 | 20 | ![Security Token](hello_token.png "sso-auth-client"). 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /auth-server/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | st.malike 6 | sso-auth-server 7 | 1.0-SNAPSHOT 8 | jar 9 | 10 | sso-auth-server 11 | http://maven.apache.org 12 | 13 | 14 | st.malike 15 | sso-auth 16 | 1.0-SNAPSHOT 17 | 18 | 19 | 20 | 21 | st.malike.auth.server.AuthServerMain 22 | 8 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | org.mockito 32 | mockito-all 33 | test 34 | 35 | 36 | 37 | uk.co.jemos.podam 38 | podam 39 | test 40 | 41 | 42 | 43 | junit 44 | junit 45 | test 46 | 47 | 48 | 49 | org.springframework 50 | spring-test 51 | test 52 | 53 | 54 | 55 | 56 | org.springframework.boot 57 | spring-boot-starter-security 58 | 59 | 60 | 61 | 62 | org.springframework.security.oauth 63 | spring-security-oauth2 64 | 65 | 66 | 67 | org.springframework.boot 68 | spring-boot-starter-tomcat 69 | 70 | 71 | 72 | org.springframework.boot 73 | spring-boot-starter-data-mongodb 74 | 75 | 76 | 77 | 78 | org.springframework.boot 79 | spring-boot-starter-web 80 | 81 | 82 | 83 | com.google.code.gson 84 | gson 85 | 86 | 87 | 88 | commons-lang 89 | commons-lang 90 | 91 | 92 | 93 | 94 | 95 | 96 | org.springframework.boot 97 | spring-boot-maven-plugin 98 | 99 | 100 | 101 | 102 | 103 | -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/AuthServerMain.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package st.malike.auth.server; 7 | 8 | import java.util.ArrayList; 9 | import java.util.List; 10 | import org.springframework.beans.factory.annotation.Autowire; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.boot.SpringApplication; 13 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 14 | import org.springframework.context.ApplicationContext; 15 | import org.springframework.context.annotation.Bean; 16 | import org.springframework.context.annotation.ComponentScan; 17 | import org.springframework.context.annotation.Configuration; 18 | import org.springframework.context.annotation.ImportResource; 19 | import org.springframework.core.convert.converter.Converter; 20 | import org.springframework.data.mongodb.MongoDbFactory; 21 | import org.springframework.data.mongodb.core.MongoTemplate; 22 | import org.springframework.data.mongodb.core.convert.CustomConversions; 23 | import org.springframework.data.mongodb.core.convert.DbRefResolver; 24 | import org.springframework.data.mongodb.core.convert.DefaultDbRefResolver; 25 | import org.springframework.data.mongodb.core.convert.MappingMongoConverter; 26 | import org.springframework.data.mongodb.core.mapping.MongoMappingContext; 27 | import org.springframework.data.mongodb.repository.config.EnableMongoRepositories; 28 | import st.malike.auth.server.config.CustomMongoDBConvertor; 29 | 30 | /** 31 | * 32 | * @author malike_st 33 | */ 34 | @Configuration 35 | @EnableAutoConfiguration 36 | @EnableMongoRepositories("st.malike.auth.server.repository") 37 | @ComponentScan 38 | @ImportResource({"classpath*:spring-security-oauth2.xml"}) 39 | public class AuthServerMain { 40 | 41 | /** 42 | * @param args the command line arguments 43 | */ 44 | public static void main(String[] args) { 45 | // TODO code application logic here 46 | ApplicationContext context = SpringApplication.run(AuthServerMain.class, args); 47 | } 48 | 49 | @Autowired 50 | private CustomMongoDBConvertor customMongoDBConvertor; 51 | @Autowired 52 | private MongoDbFactory mongoDbFactory; 53 | 54 | @Bean 55 | public CustomConversions customConversions() { 56 | List> converterList = new ArrayList<>(); 57 | converterList.add(customMongoDBConvertor); 58 | return new CustomConversions(converterList); 59 | } 60 | 61 | @Bean 62 | public MappingMongoConverter mongoConverter() throws Exception { 63 | MongoMappingContext mappingContext = new MongoMappingContext(); 64 | DbRefResolver dbRefResolver = new DefaultDbRefResolver(mongoDbFactory); 65 | MappingMongoConverter mongoConverter = new MappingMongoConverter(dbRefResolver, mappingContext); 66 | mongoConverter.setCustomConversions(customConversions()); 67 | return mongoConverter; 68 | } 69 | 70 | @Bean(autowire = Autowire.BY_NAME, name = "mongoTemplate") 71 | public MongoTemplate customMongoTemplate() { 72 | try { 73 | return new MongoTemplate(mongoDbFactory, mongoConverter()); // a mongotemplate with custom convertor 74 | } catch (Exception e) { 75 | } 76 | return null; 77 | } 78 | 79 | } 80 | -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/config/ClientAuthenticationToken.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package st.malike.auth.server.config; 7 | 8 | import java.util.Collection; 9 | import org.springframework.security.authentication.AbstractAuthenticationToken; 10 | import org.springframework.security.core.GrantedAuthority; 11 | 12 | /** 13 | * 14 | * @author malike_st 15 | */ 16 | public class ClientAuthenticationToken extends AbstractAuthenticationToken { 17 | 18 | private final Object principal; 19 | private final Object credentials; 20 | private final boolean client; 21 | 22 | public ClientAuthenticationToken(Object principal, Object credentials, Collection authorities) { 23 | super(authorities); 24 | this.principal = principal; 25 | this.credentials = credentials; 26 | this.client = true; 27 | super.setAuthenticated(true); 28 | } 29 | 30 | @Override 31 | public Object getCredentials() { 32 | return this.credentials; 33 | } 34 | 35 | @Override 36 | public Object getPrincipal() { 37 | return this.principal; 38 | } 39 | 40 | public boolean isClient() { 41 | return client; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/config/CustomMongoDBConvertor.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package st.malike.auth.server.config; 6 | 7 | import com.mongodb.DBObject; 8 | import java.util.ArrayList; 9 | import java.util.HashSet; 10 | import java.util.List; 11 | import java.util.Map; 12 | import org.springframework.beans.factory.annotation.Autowired; 13 | import org.springframework.context.annotation.Bean; 14 | import org.springframework.context.annotation.Configuration; 15 | import org.springframework.core.convert.converter.Converter; 16 | import org.springframework.data.mongodb.core.convert.CustomConversions; 17 | import org.springframework.security.core.Authentication; 18 | import org.springframework.security.oauth2.provider.ClientDetails; 19 | import org.springframework.security.oauth2.provider.OAuth2Authentication; 20 | import org.springframework.security.oauth2.provider.OAuth2Request; 21 | import st.malike.auth.server.model.User; 22 | import st.malike.auth.server.service.security.ClientDetailService; 23 | import st.malike.auth.server.service.security.UserAuthConfigService; 24 | 25 | /** 26 | * 27 | * 28 | * @author malike_st 29 | */ 30 | @Configuration 31 | public class CustomMongoDBConvertor implements Converter { 32 | 33 | @Autowired 34 | private UserAuthConfigService authConfigService; 35 | @Autowired 36 | private ClientDetailService clientDetailService; 37 | 38 | 39 | 40 | @Override 41 | public OAuth2Authentication convert(DBObject source) { 42 | DBObject storedRequest = (DBObject) source.get("storedRequest"); 43 | OAuth2Request oAuth2Request = new OAuth2Request((Map) storedRequest.get("requestParameters"), 44 | (String) storedRequest.get("clientId"), null, true, new HashSet((List) storedRequest.get("scope")), 45 | null, null, null, null); 46 | DBObject userAuthorization = (DBObject) source.get("userAuthentication"); 47 | if (null != userAuthorization) { //its a user 48 | Object prinObj = userAuthorization.get("principal"); 49 | User u = null; 50 | if ((null != prinObj) && prinObj instanceof String) { 51 | u = authConfigService.getUser((String) prinObj); 52 | } else if (null != prinObj) { 53 | DBObject principalDBO = (DBObject) prinObj; 54 | String email = (String) principalDBO.get("username"); 55 | u = authConfigService.getUser(email); 56 | } 57 | if (null == u) { 58 | return null; 59 | } 60 | 61 | Authentication userAuthentication = new UserAuthenticationToken(u.getEmail(), 62 | (String) userAuthorization.get("credentials"), authConfigService.getRights(u)); 63 | OAuth2Authentication authentication = new OAuth2Authentication(oAuth2Request, userAuthentication); 64 | return authentication; 65 | } else { //its a client 66 | String clientId = (String) storedRequest.get("clientId"); 67 | ClientDetails client = null; 68 | if ((null != clientId) && clientId instanceof String) { 69 | client = clientDetailService.loadClientByClientId(clientId); 70 | } 71 | if (null == client) { 72 | return null; 73 | } 74 | Authentication userAuthentication = new ClientAuthenticationToken(client.getClientId(), 75 | null, client.getAuthorities()); 76 | return new OAuth2Authentication(oAuth2Request, userAuthentication); 77 | } 78 | } 79 | 80 | @Bean 81 | public CustomConversions customConversions() { 82 | List> converterList = new ArrayList<>(); 83 | converterList.add(this); 84 | return new CustomConversions(converterList); 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/config/UserAuthenticationToken.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package st.malike.auth.server.config; 6 | 7 | import java.util.Collection; 8 | import org.springframework.security.authentication.AbstractAuthenticationToken; 9 | import org.springframework.security.core.GrantedAuthority; 10 | 11 | /** 12 | * 13 | * 14 | * @author malike_st 15 | */ 16 | public class UserAuthenticationToken extends AbstractAuthenticationToken { 17 | 18 | private final Object principal; 19 | private final Object credentials; 20 | private final boolean client; 21 | 22 | public UserAuthenticationToken(Object principal, Object credentials, Collection authorities) { 23 | super(authorities); 24 | this.principal = principal; 25 | this.credentials = credentials; 26 | this.client = false; 27 | super.setAuthenticated(true); 28 | } 29 | 30 | @Override 31 | public Object getCredentials() { 32 | return this.credentials; 33 | } 34 | 35 | @Override 36 | public Object getPrincipal() { 37 | return this.principal; 38 | } 39 | 40 | public boolean isClient() { 41 | return client; 42 | } 43 | 44 | } 45 | -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/exception/AuthenticationException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package st.malike.auth.server.exception; 7 | 8 | /** 9 | * 10 | * @author malike_st 11 | */ 12 | public class AuthenticationException extends Exception { 13 | 14 | public AuthenticationException() { 15 | } 16 | 17 | public AuthenticationException(String message) { 18 | super(message); 19 | } 20 | 21 | public AuthenticationException(String message, Throwable cause) { 22 | super(message, cause); 23 | } 24 | 25 | public AuthenticationException(Throwable cause) { 26 | super(cause); 27 | } 28 | 29 | public AuthenticationException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 30 | super(message, cause, enableSuppression, writableStackTrace); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/exception/InvalidClientException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package st.malike.auth.server.exception; 7 | 8 | /** 9 | * 10 | * @author malike_st 11 | */ 12 | public class InvalidClientException extends Exception { 13 | 14 | public InvalidClientException() { 15 | } 16 | 17 | public InvalidClientException(String message) { 18 | super(message); 19 | } 20 | 21 | public InvalidClientException(String message, Throwable cause) { 22 | super(message, cause); 23 | } 24 | 25 | public InvalidClientException(Throwable cause) { 26 | super(cause); 27 | } 28 | 29 | public InvalidClientException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 30 | super(message, cause, enableSuppression, writableStackTrace); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/exception/InvalidUserException.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package st.malike.auth.server.exception; 7 | 8 | /** 9 | * 10 | * @author malike_st 11 | */ 12 | public class InvalidUserException extends Exception { 13 | 14 | public InvalidUserException() { 15 | } 16 | 17 | public InvalidUserException(String message) { 18 | super(message); 19 | } 20 | 21 | public InvalidUserException(String message, Throwable cause) { 22 | super(message, cause); 23 | } 24 | 25 | public InvalidUserException(Throwable cause) { 26 | super(cause); 27 | } 28 | 29 | public InvalidUserException(String message, Throwable cause, boolean enableSuppression, boolean writableStackTrace) { 30 | super(message, cause, enableSuppression, writableStackTrace); 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/model/ClientDetail.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package st.malike.auth.server.model; 7 | 8 | import java.util.Collection; 9 | import java.util.Map; 10 | import java.util.Set; 11 | import org.springframework.data.annotation.Id; 12 | import org.springframework.data.mongodb.core.index.Indexed; 13 | import org.springframework.data.mongodb.core.mapping.Document; 14 | 15 | /** 16 | * 17 | * @author malike_st 18 | */ 19 | @Document(collection = "oauth2_client") 20 | public class ClientDetail { 21 | 22 | @Id 23 | private String id; 24 | @Indexed 25 | private String clientId; 26 | private Set resourceIds; 27 | private boolean secretRequired; 28 | @Indexed 29 | private String clientSecret; 30 | private boolean scoped; 31 | private Set scope; 32 | private Set authorizedGrantTypes; 33 | private Set registeredRedirectUri; 34 | private Collection authorities; 35 | private Integer accessTokenValiditySeconds; 36 | private Integer refreshTokenValiditySeconds; 37 | private boolean autoApprove; 38 | private Map additionalInformation; 39 | 40 | public ClientDetail() { 41 | } 42 | 43 | public String getId() { 44 | return id; 45 | } 46 | 47 | public void setId(String id) { 48 | this.id = id; 49 | } 50 | 51 | public String getClientId() { 52 | return clientId; 53 | } 54 | 55 | public void setClientId(String clientId) { 56 | this.clientId = clientId; 57 | } 58 | 59 | public Set getResourceIds() { 60 | return resourceIds; 61 | } 62 | 63 | public void setResourceIds(Set resourceIds) { 64 | this.resourceIds = resourceIds; 65 | } 66 | 67 | public boolean isSecretRequired() { 68 | return secretRequired; 69 | } 70 | 71 | public Set getScope() { 72 | return scope; 73 | } 74 | 75 | public void setScope(Set scope) { 76 | this.scope = scope; 77 | } 78 | 79 | public void setSecretRequired(boolean secretRequired) { 80 | this.secretRequired = secretRequired; 81 | } 82 | 83 | public String getClientSecret() { 84 | return clientSecret; 85 | } 86 | 87 | public void setClientSecret(String clientSecret) { 88 | this.clientSecret = clientSecret; 89 | } 90 | 91 | public boolean isScoped() { 92 | return scoped; 93 | } 94 | 95 | public void setScoped(boolean scoped) { 96 | this.scoped = scoped; 97 | } 98 | 99 | public Set getAuthorizedGrantTypes() { 100 | return authorizedGrantTypes; 101 | } 102 | 103 | public void setAuthorizedGrantTypes(Set authorizedGrantTypes) { 104 | this.authorizedGrantTypes = authorizedGrantTypes; 105 | } 106 | 107 | public Set getRegisteredRedirectUri() { 108 | return registeredRedirectUri; 109 | } 110 | 111 | public void setRegisteredRedirectUri(Set registeredRedirectUri) { 112 | this.registeredRedirectUri = registeredRedirectUri; 113 | } 114 | 115 | public Collection getAuthorities() { 116 | return authorities; 117 | } 118 | 119 | public void setAuthorities(Collection authorities) { 120 | this.authorities = authorities; 121 | } 122 | 123 | public Integer getAccessTokenValiditySeconds() { 124 | return accessTokenValiditySeconds; 125 | } 126 | 127 | public void setAccessTokenValiditySeconds(Integer accessTokenValiditySeconds) { 128 | this.accessTokenValiditySeconds = accessTokenValiditySeconds; 129 | } 130 | 131 | public Integer getRefreshTokenValiditySeconds() { 132 | return refreshTokenValiditySeconds; 133 | } 134 | 135 | public void setRefreshTokenValiditySeconds(Integer refreshTokenValiditySeconds) { 136 | this.refreshTokenValiditySeconds = refreshTokenValiditySeconds; 137 | } 138 | 139 | public boolean isAutoApprove() { 140 | return autoApprove; 141 | } 142 | 143 | public void setAutoApprove(boolean autoApprove) { 144 | this.autoApprove = autoApprove; 145 | } 146 | 147 | public Map getAdditionalInformation() { 148 | return additionalInformation; 149 | } 150 | 151 | public void setAdditionalInformation(Map additionalInformation) { 152 | this.additionalInformation = additionalInformation; 153 | } 154 | 155 | } 156 | -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/model/OAuth2AuthenticationAccessToken.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package st.malike.auth.server.model; 6 | 7 | import java.io.Serializable; 8 | import org.springframework.data.mongodb.core.index.Indexed; 9 | import org.springframework.data.mongodb.core.mapping.Document; 10 | import org.springframework.security.oauth2.common.OAuth2AccessToken; 11 | import org.springframework.security.oauth2.provider.OAuth2Authentication; 12 | 13 | /** 14 | * 15 | * @author malike_st 16 | */ 17 | @Document(collection = "oauth2_access_token") 18 | public class OAuth2AuthenticationAccessToken implements Serializable { 19 | 20 | @Indexed 21 | private String id; 22 | private String tokenId; 23 | private OAuth2AccessToken oAuth2AccessToken; 24 | private String authenticationId; 25 | private String userName; 26 | private String clientId; 27 | private OAuth2Authentication authentication; 28 | private String refreshToken; 29 | 30 | public OAuth2AuthenticationAccessToken() { 31 | } 32 | 33 | public OAuth2AuthenticationAccessToken(final OAuth2AccessToken oAuth2AccessToken, final OAuth2Authentication authentication, final String authenticationId) { 34 | this.tokenId = oAuth2AccessToken.getValue(); 35 | this.oAuth2AccessToken = oAuth2AccessToken; 36 | this.authenticationId = authenticationId; 37 | this.userName = authentication.getName(); 38 | this.clientId = authentication.getOAuth2Request().getClientId(); 39 | this.authentication = authentication; 40 | this.refreshToken = oAuth2AccessToken.getRefreshToken().getValue(); 41 | } 42 | 43 | public String getId() { 44 | return id; 45 | } 46 | 47 | public void setId(String id) { 48 | this.id = id; 49 | } 50 | 51 | public String getTokenId() { 52 | return tokenId; 53 | } 54 | 55 | public OAuth2AccessToken getoAuth2AccessToken() { 56 | return oAuth2AccessToken; 57 | } 58 | 59 | public String getAuthenticationId() { 60 | return authenticationId; 61 | } 62 | 63 | public String getUserName() { 64 | return userName; 65 | } 66 | 67 | public String getClientId() { 68 | return clientId; 69 | } 70 | 71 | public OAuth2Authentication getAuthentication() { 72 | return authentication; 73 | } 74 | 75 | public String getRefreshToken() { 76 | return refreshToken; 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/model/OAuth2AuthenticationRefreshToken.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package st.malike.auth.server.model; 6 | 7 | import java.io.Serializable; 8 | import org.springframework.data.mongodb.core.index.Indexed; 9 | import org.springframework.data.mongodb.core.mapping.Document; 10 | import org.springframework.security.oauth2.common.OAuth2RefreshToken; 11 | import org.springframework.security.oauth2.provider.OAuth2Authentication; 12 | 13 | /** 14 | * 15 | * @author malike_st 16 | */ 17 | @Document(collection = "oauth2_refresh_token") 18 | public class OAuth2AuthenticationRefreshToken implements Serializable { 19 | 20 | @Indexed 21 | private String id; 22 | private final String tokenId; 23 | private final OAuth2RefreshToken oAuth2RefreshToken; 24 | private final OAuth2Authentication authentication; 25 | 26 | public OAuth2AuthenticationRefreshToken(OAuth2RefreshToken oAuth2RefreshToken, OAuth2Authentication authentication) { 27 | this.oAuth2RefreshToken = oAuth2RefreshToken; 28 | this.authentication = authentication; 29 | this.tokenId = oAuth2RefreshToken.getValue(); 30 | } 31 | 32 | public String getId() { 33 | return id; 34 | } 35 | 36 | public void setId(String id) { 37 | this.id = id; 38 | } 39 | 40 | public String getTokenId() { 41 | return tokenId; 42 | } 43 | 44 | public OAuth2RefreshToken getoAuth2RefreshToken() { 45 | return oAuth2RefreshToken; 46 | } 47 | 48 | public OAuth2Authentication getAuthentication() { 49 | return authentication; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/model/User.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package st.malike.auth.server.model; 7 | 8 | import java.util.Date; 9 | import java.util.List; 10 | import org.springframework.data.annotation.CreatedDate; 11 | import org.springframework.data.annotation.Id; 12 | import org.springframework.data.mongodb.core.mapping.Document; 13 | 14 | /** 15 | * 16 | * @author malike_st 17 | */ 18 | @Document(collection = "oauth2_user") 19 | public class User { 20 | 21 | @Id 22 | private String id; 23 | private String email; 24 | private String password; 25 | private List rights; 26 | @CreatedDate 27 | private Date dateCreated; 28 | 29 | public User() { 30 | } 31 | 32 | public String getId() { 33 | return id; 34 | } 35 | 36 | public void setId(String id) { 37 | this.id = id; 38 | } 39 | 40 | public String getEmail() { 41 | return email; 42 | } 43 | 44 | public void setEmail(String email) { 45 | this.email = email; 46 | } 47 | 48 | public String getPassword() { 49 | return password; 50 | } 51 | 52 | public void setPassword(String password) { 53 | this.password = password; 54 | } 55 | 56 | public Date getDateCreated() { 57 | return dateCreated; 58 | } 59 | 60 | public void setDateCreated(Date dateCreated) { 61 | this.dateCreated = dateCreated; 62 | } 63 | 64 | public List getRights() { 65 | return rights; 66 | } 67 | 68 | public void setRights(List rights) { 69 | this.rights = rights; 70 | } 71 | 72 | } 73 | -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/repository/ClientDetailRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package st.malike.auth.server.repository; 7 | 8 | import java.io.Serializable; 9 | import org.springframework.data.mongodb.repository.MongoRepository; 10 | import st.malike.auth.server.model.ClientDetail; 11 | 12 | /** 13 | * 14 | * @author malike_st 15 | */ 16 | public interface ClientDetailRepository extends MongoRepository { 17 | 18 | public ClientDetail findByClientId(String clientId); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/repository/OAuth2AccessTokenRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | 6 | package st.malike.auth.server.repository; 7 | 8 | import java.io.Serializable; 9 | import org.springframework.data.mongodb.repository.MongoRepository; 10 | import st.malike.auth.server.model.OAuth2AuthenticationAccessToken; 11 | 12 | /** 13 | * 14 | * @author malike_st 15 | */ 16 | public interface OAuth2AccessTokenRepository extends MongoRepository { 17 | 18 | } 19 | -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/repository/OAuth2RefreshTokenRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package st.malike.auth.server.repository; 6 | 7 | import org.springframework.data.mongodb.repository.MongoRepository; 8 | import st.malike.auth.server.model.OAuth2AuthenticationRefreshToken; 9 | 10 | /** 11 | * 12 | * @author malike_st 13 | */ 14 | 15 | public interface OAuth2RefreshTokenRepository extends MongoRepository { 16 | 17 | 18 | } -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/repository/UserRepository.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package st.malike.auth.server.repository; 7 | 8 | import java.io.Serializable; 9 | import org.springframework.data.mongodb.repository.MongoRepository; 10 | import st.malike.auth.server.model.User; 11 | 12 | /** 13 | * 14 | * @author malike_st 15 | */ 16 | public interface UserRepository extends MongoRepository { 17 | 18 | public User findByEmail(String email); 19 | 20 | } 21 | -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/service/UserService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package st.malike.auth.server.service; 7 | 8 | import org.springframework.beans.factory.annotation.Autowired; 9 | import org.springframework.stereotype.Service; 10 | import st.malike.auth.server.model.User; 11 | import st.malike.auth.server.repository.UserRepository; 12 | 13 | /** 14 | * 15 | * @author malike_st 16 | */ 17 | @Service 18 | public class UserService { 19 | 20 | @Autowired 21 | private UserRepository userRepository; 22 | 23 | public User getUserById(String id) { 24 | return userRepository.findOne(id); 25 | } 26 | 27 | public User save(User user) { 28 | return userRepository.save(user); 29 | } 30 | 31 | public User findByEmail(String email) { 32 | return userRepository.findByEmail(email); 33 | } 34 | 35 | public void deleteAll() { 36 | userRepository.deleteAll(); 37 | } 38 | 39 | } 40 | -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/service/security/ClientDetailService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package st.malike.auth.server.service.security; 7 | 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.security.crypto.password.PasswordEncoder; 12 | import org.springframework.security.oauth2.provider.ClientAlreadyExistsException; 13 | import org.springframework.security.oauth2.provider.ClientDetails; 14 | import org.springframework.security.oauth2.provider.ClientDetailsService; 15 | import org.springframework.security.oauth2.provider.ClientRegistrationException; 16 | import org.springframework.security.oauth2.provider.ClientRegistrationService; 17 | import org.springframework.security.oauth2.provider.NoSuchClientException; 18 | import org.springframework.security.oauth2.provider.client.BaseClientDetails; 19 | import org.springframework.stereotype.Service; 20 | import st.malike.auth.server.model.ClientDetail; 21 | import st.malike.auth.server.repository.ClientDetailRepository; 22 | 23 | /** 24 | * 25 | * @author malike_st 26 | */ 27 | @Service 28 | public class ClientDetailService implements ClientDetailsService, ClientRegistrationService { 29 | 30 | @Autowired 31 | private PasswordEncoder passwordEncoder; 32 | @Autowired 33 | private ClientDetailRepository clientDetailsRepository; 34 | 35 | @Override 36 | public ClientDetails loadClientByClientId(String clientId) throws ClientRegistrationException { 37 | ClientDetail clientDetails = clientDetailsRepository.findByClientId(clientId); 38 | if (null == clientDetails) { 39 | throw new ClientRegistrationException("Client not found with id '" + clientId + "'"); 40 | } 41 | return getClientFromMongoDBClientDetails(clientDetails); 42 | } 43 | 44 | @Override 45 | public void addClientDetails(ClientDetails cd) throws ClientAlreadyExistsException { 46 | ClientDetail clientDetails = getMongoDBClientDetailsFromClient(cd); 47 | clientDetailsRepository.save(clientDetails); 48 | } 49 | 50 | @Override 51 | public void updateClientDetails(ClientDetails cd) throws NoSuchClientException { 52 | ClientDetail clientDetails = clientDetailsRepository.findByClientId(cd.getClientId()); 53 | if (null == clientDetails) { 54 | throw new NoSuchClientException("Client not found with ID '" + cd.getClientId() + "'"); 55 | } 56 | clientDetails = getMongoDBClientDetailsFromClient(cd); 57 | clientDetailsRepository.save(clientDetails); 58 | } 59 | 60 | @Override 61 | public void updateClientSecret(String clientId, String secret) throws NoSuchClientException { 62 | ClientDetail clientDetails = clientDetailsRepository.findByClientId(clientId); 63 | if (null == clientDetails) { 64 | throw new NoSuchClientException("Client not found with ID '" + clientId + "'"); 65 | } 66 | clientDetails.setClientSecret(passwordEncoder.encode(secret)); 67 | clientDetailsRepository.save(clientDetails); 68 | } 69 | 70 | @Override 71 | public void removeClientDetails(String clientId) throws NoSuchClientException { 72 | ClientDetail clientDetails = clientDetailsRepository.findByClientId(clientId); 73 | if (null == clientDetails) { 74 | throw new NoSuchClientException("Client not found with ID '" + clientId + "'"); 75 | } 76 | clientDetailsRepository.delete(clientDetails); 77 | } 78 | 79 | @Override 80 | public List listClientDetails() { 81 | List mdbcds = clientDetailsRepository.findAll(); 82 | return getClientsFromMongoDBClientDetails(mdbcds); 83 | } 84 | 85 | private List getClientsFromMongoDBClientDetails(List clientDetails) { 86 | List bcds = new LinkedList<>(); 87 | if (clientDetails != null && !clientDetails.isEmpty()) { 88 | clientDetails.stream().forEach(mdbcd -> { 89 | bcds.add(getClientFromMongoDBClientDetails(mdbcd)); 90 | }); 91 | } 92 | return bcds; 93 | } 94 | 95 | private BaseClientDetails getClientFromMongoDBClientDetails(ClientDetail clientDetails) { 96 | BaseClientDetails bc = new BaseClientDetails(); 97 | bc.setAccessTokenValiditySeconds(clientDetails.getAccessTokenValiditySeconds()); 98 | bc.setAuthorizedGrantTypes(clientDetails.getAuthorizedGrantTypes()); 99 | bc.setClientId(clientDetails.getClientId()); 100 | bc.setClientSecret(clientDetails.getClientSecret()); 101 | bc.setRefreshTokenValiditySeconds(clientDetails.getRefreshTokenValiditySeconds()); 102 | bc.setRegisteredRedirectUri(clientDetails.getRegisteredRedirectUri()); 103 | bc.setResourceIds(clientDetails.getResourceIds()); 104 | bc.setScope(clientDetails.getScope()); 105 | return bc; 106 | } 107 | 108 | private ClientDetail getMongoDBClientDetailsFromClient(ClientDetails cd) { 109 | ClientDetail clientDetails = new ClientDetail(); 110 | clientDetails.setAccessTokenValiditySeconds(cd.getAccessTokenValiditySeconds()); 111 | clientDetails.setAdditionalInformation(cd.getAdditionalInformation()); 112 | clientDetails.setAuthorizedGrantTypes(cd.getAuthorizedGrantTypes()); 113 | clientDetails.setClientId(cd.getClientId()); 114 | clientDetails.setClientSecret(cd.getClientSecret()); 115 | clientDetails.setRefreshTokenValiditySeconds(cd.getRefreshTokenValiditySeconds()); 116 | clientDetails.setRegisteredRedirectUri(cd.getRegisteredRedirectUri()); 117 | clientDetails.setResourceIds(cd.getResourceIds()); 118 | clientDetails.setScope(cd.getScope()); 119 | clientDetails.setScoped(cd.isScoped()); 120 | clientDetails.setSecretRequired(cd.isSecretRequired()); 121 | clientDetails.setId(cd.getClientId()); 122 | return clientDetails; 123 | } 124 | 125 | public ClientDetail save(ClientDetail authClient) { 126 | return clientDetailsRepository.save(authClient); 127 | } 128 | 129 | public void deleteAll() { 130 | clientDetailsRepository.deleteAll(); 131 | } 132 | 133 | } 134 | -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/service/security/TokenStoreService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package st.malike.auth.server.service.security; 7 | 8 | import java.util.ArrayList; 9 | import java.util.Collection; 10 | import java.util.List; 11 | import org.springframework.beans.factory.annotation.Autowired; 12 | import org.springframework.data.mongodb.core.MongoTemplate; 13 | import org.springframework.data.mongodb.core.query.Criteria; 14 | import org.springframework.data.mongodb.core.query.Query; 15 | import org.springframework.security.oauth2.common.OAuth2AccessToken; 16 | import org.springframework.security.oauth2.common.OAuth2RefreshToken; 17 | import org.springframework.security.oauth2.common.exceptions.InvalidTokenException; 18 | import org.springframework.security.oauth2.provider.OAuth2Authentication; 19 | import org.springframework.security.oauth2.provider.token.AuthenticationKeyGenerator; 20 | import org.springframework.security.oauth2.provider.token.DefaultAuthenticationKeyGenerator; 21 | import org.springframework.security.oauth2.provider.token.TokenStore; 22 | import st.malike.auth.server.model.OAuth2AuthenticationAccessToken; 23 | import st.malike.auth.server.model.OAuth2AuthenticationRefreshToken; 24 | import st.malike.auth.server.repository.OAuth2AccessTokenRepository; 25 | import st.malike.auth.server.repository.OAuth2RefreshTokenRepository; 26 | 27 | /** 28 | * 29 | * @author malike_st 30 | */ 31 | public class TokenStoreService implements TokenStore { 32 | 33 | @Autowired 34 | private OAuth2AccessTokenRepository oAuth2AccessTokenRepository; 35 | @Autowired 36 | private OAuth2RefreshTokenRepository oAuth2RefreshTokenRepository; 37 | @Autowired 38 | private MongoTemplate mongoTemplate; 39 | private final AuthenticationKeyGenerator authenticationKeyGenerator = new DefaultAuthenticationKeyGenerator(); 40 | 41 | @Override 42 | public OAuth2Authentication readAuthentication(OAuth2AccessToken token) { 43 | return readAuthentication(token.getValue()); 44 | } 45 | 46 | @Override 47 | public OAuth2Authentication readAuthentication(String tokenId) { 48 | Query query = new Query(); 49 | query.addCriteria(Criteria.where("tokenId").is(tokenId)); 50 | OAuth2AuthenticationAccessToken token = mongoTemplate.findOne(query, OAuth2AuthenticationAccessToken.class, "oauth2_access_token"); 51 | return null == token ? null : token.getAuthentication(); 52 | } 53 | 54 | @Override 55 | public void storeAccessToken(OAuth2AccessToken token, OAuth2Authentication authentication) { 56 | OAuth2AuthenticationAccessToken oAuth2AuthenticationAccessToken = new OAuth2AuthenticationAccessToken(token, 57 | authentication, authenticationKeyGenerator.extractKey(authentication)); 58 | mongoTemplate.save(oAuth2AuthenticationAccessToken); 59 | } 60 | 61 | @Override 62 | public OAuth2AccessToken readAccessToken(String tokenId) { 63 | Query query = new Query(); 64 | query.addCriteria(Criteria.where("tokenId").is(tokenId)); 65 | OAuth2AuthenticationAccessToken token = mongoTemplate.findOne(query, OAuth2AuthenticationAccessToken.class, "oauth2_access_token"); 66 | if (null == token) { 67 | throw new InvalidTokenException("Token not valid"); 68 | } 69 | return token.getoAuth2AccessToken(); 70 | } 71 | 72 | @Override 73 | public void removeAccessToken(OAuth2AccessToken accessToken) { 74 | Query query = new Query(); 75 | query.addCriteria(Criteria.where("tokenId").is(accessToken.getValue())); 76 | OAuth2AuthenticationAccessToken token = mongoTemplate.findOne(query, OAuth2AuthenticationAccessToken.class, "oauth2_access_token"); 77 | if (token != null) { 78 | oAuth2AccessTokenRepository.delete(token); 79 | } 80 | } 81 | 82 | @Override 83 | public void storeRefreshToken(OAuth2RefreshToken refreshToken, OAuth2Authentication authentication) { 84 | oAuth2RefreshTokenRepository.save(new OAuth2AuthenticationRefreshToken(refreshToken, authentication)); 85 | } 86 | 87 | @Override 88 | public OAuth2RefreshToken readRefreshToken(String accessToken) { 89 | Query query = new Query(); 90 | query.addCriteria(Criteria.where("tokenId").is(accessToken)); 91 | OAuth2AuthenticationRefreshToken token = mongoTemplate.findOne(query, OAuth2AuthenticationRefreshToken.class, "oauth2_refresh_token"); 92 | return token.getoAuth2RefreshToken(); 93 | } 94 | 95 | @Override 96 | public OAuth2Authentication readAuthenticationForRefreshToken(OAuth2RefreshToken token) { 97 | Query query = new Query(); 98 | query.addCriteria(Criteria.where("tokenId").is(token.getValue())); 99 | OAuth2AuthenticationRefreshToken auth2AuthenticationRefreshToken = mongoTemplate.findOne(query, OAuth2AuthenticationRefreshToken.class, "oauth2_refresh_token"); 100 | return auth2AuthenticationRefreshToken.getAuthentication(); 101 | } 102 | 103 | @Override 104 | public void removeRefreshToken(OAuth2RefreshToken accessToken) { 105 | Query query = new Query(); 106 | query.addCriteria(Criteria.where("tokenId").is(accessToken.getValue())); 107 | OAuth2AuthenticationRefreshToken token = mongoTemplate.findOne(query, OAuth2AuthenticationRefreshToken.class, "oauth2_refresh_token"); 108 | if (token != null) { 109 | oAuth2RefreshTokenRepository.delete(token); 110 | } 111 | } 112 | 113 | @Override 114 | public void removeAccessTokenUsingRefreshToken(OAuth2RefreshToken refreshToken) { 115 | Query query = new Query(); 116 | query.addCriteria(Criteria.where("refreshToken").is(refreshToken.getValue())); 117 | OAuth2AuthenticationAccessToken token = mongoTemplate.findOne(query, OAuth2AuthenticationAccessToken.class, "oauth2_access_token"); 118 | if (token != null) { 119 | oAuth2AccessTokenRepository.delete(token); 120 | } 121 | } 122 | 123 | @Override 124 | public OAuth2AccessToken getAccessToken(OAuth2Authentication authentication) { 125 | String authenticationId = authenticationKeyGenerator.extractKey(authentication); 126 | if (null == authenticationId) { 127 | return null; 128 | } 129 | Query query = new Query(); 130 | query.addCriteria(Criteria.where("authenticationId").is(authenticationId)); 131 | OAuth2AuthenticationAccessToken token = mongoTemplate.findOne(query, OAuth2AuthenticationAccessToken.class, "oauth2_access_token"); 132 | return token == null ? null : token.getoAuth2AccessToken(); 133 | } 134 | 135 | @Override 136 | public Collection findTokensByClientId(String clientId) { 137 | Query query = new Query(); 138 | query.addCriteria(Criteria.where("clientId").is(clientId)); 139 | List accessTokens = mongoTemplate.find(query, OAuth2AuthenticationAccessToken.class, "oauth2_access_token"); 140 | return extractAccessTokens(accessTokens); 141 | } 142 | 143 | @Override 144 | public Collection findTokensByClientIdAndUserName(String clientId, String userName) { 145 | Query query = new Query(); 146 | query.addCriteria(Criteria.where("clientId").is(clientId)); 147 | query.addCriteria(Criteria.where("userName").is(userName)); 148 | List accessTokens = mongoTemplate.find(query, OAuth2AuthenticationAccessToken.class, "oauth2_access_token"); 149 | return extractAccessTokens(accessTokens); 150 | } 151 | 152 | private Collection extractAccessTokens(List tokens) { 153 | List accessTokens = new ArrayList<>(); 154 | tokens.stream().forEach(token -> { 155 | accessTokens.add(token.getoAuth2AccessToken()); 156 | }); 157 | return accessTokens; 158 | } 159 | 160 | } 161 | -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/service/security/UserAuthConfigService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package st.malike.auth.server.service.security; 7 | 8 | import java.util.LinkedList; 9 | import java.util.List; 10 | import org.springframework.beans.factory.annotation.Autowired; 11 | import org.springframework.security.core.GrantedAuthority; 12 | import org.springframework.security.core.authority.SimpleGrantedAuthority; 13 | import org.springframework.stereotype.Service; 14 | import st.malike.auth.server.model.User; 15 | import st.malike.auth.server.service.UserService; 16 | 17 | /** 18 | * 19 | * @author malike_st 20 | */ 21 | @Service 22 | public class UserAuthConfigService { 23 | 24 | @Autowired 25 | private UserService userService; 26 | 27 | public User getUser(String email) { 28 | return userService.findByEmail(email); 29 | } 30 | 31 | public List getRights(User user) { 32 | List grantedAuthority = new LinkedList<>(); 33 | List right = user.getRights(); 34 | if (null != right && !right.isEmpty()) { 35 | right.stream().forEach(r -> { 36 | grantedAuthority.add(new SimpleGrantedAuthority(r)); 37 | }); 38 | } 39 | return grantedAuthority; 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/service/security/UserAuthProviderService.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package st.malike.auth.server.service.security; 7 | 8 | import java.util.List; 9 | import org.springframework.beans.factory.annotation.Autowired; 10 | import org.springframework.security.authentication.AuthenticationProvider; 11 | import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; 12 | import org.springframework.security.core.Authentication; 13 | import org.springframework.security.core.AuthenticationException; 14 | import org.springframework.security.core.GrantedAuthority; 15 | import org.springframework.security.core.context.SecurityContextHolder; 16 | import org.springframework.security.core.userdetails.UserDetails; 17 | import org.springframework.security.crypto.password.PasswordEncoder; 18 | import org.springframework.stereotype.Component; 19 | import st.malike.auth.server.model.User; 20 | 21 | /** 22 | * 23 | * @author malike_st 24 | */ 25 | @Component 26 | public class UserAuthProviderService implements AuthenticationProvider { 27 | 28 | @Autowired 29 | private UserAuthConfigService authConfigService; 30 | @Autowired 31 | private PasswordEncoder passwordEncoder; 32 | 33 | 34 | private Authentication signInUser(User user, List roles) { 35 | UserDetails springSecurityUser = new org.springframework.security.core.userdetails.User(user.getEmail(), user.getId(), roles); 36 | Authentication authentication = new UsernamePasswordAuthenticationToken(springSecurityUser, user.getId(), roles); 37 | SecurityContextHolder.getContext().setAuthentication(authentication); 38 | return authentication; 39 | } 40 | 41 | @Override 42 | public Authentication authenticate(Authentication a) throws AuthenticationException { 43 | String email = a.getName(); 44 | String password = a.getCredentials().toString(); 45 | User user = authConfigService.getUser(email); 46 | if (null != user) { 47 | if (passwordEncoder.matches(password, user.getPassword())) { 48 | List roleAuthority = authConfigService.getRights(user); 49 | return signInUser(user, roleAuthority); 50 | } 51 | throw new AuthenticationException("Password for '" + email + "' not correct.") { 52 | }; 53 | } 54 | 55 | throw new AuthenticationException("Could not find user with name '" + email + "'") { 56 | }; 57 | } 58 | 59 | @Override 60 | public boolean supports(Class type) { 61 | return type.equals(UsernamePasswordAuthenticationToken.class); 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /auth-server/src/main/java/st/malike/auth/server/util/CORSFilter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this template, choose Tools | Templates 3 | * and open the template in the editor. 4 | */ 5 | package st.malike.auth.server.util; 6 | 7 | import org.springframework.stereotype.Component; 8 | 9 | import javax.servlet.*; 10 | import javax.servlet.http.HttpServletResponse; 11 | import java.io.IOException; 12 | 13 | /** 14 | * 15 | * @author malike_st 16 | */ 17 | @Component 18 | public class CORSFilter implements Filter { 19 | 20 | @Override 21 | public void init(FilterConfig filterConfig) { 22 | } 23 | 24 | @Override 25 | public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException { 26 | HttpServletResponse response = (HttpServletResponse) res; 27 | response.setHeader("Access-Control-Allow-Origin", "*"); 28 | response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE"); 29 | response.setHeader("Access-Control-Max-Age", "3600"); 30 | response.setHeader("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept"); 31 | chain.doFilter(req, res); 32 | } 33 | 34 | @Override 35 | public void destroy() { 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /auth-server/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | server.port=8080 3 | 4 | 5 | security.basic.enabled=false 6 | 7 | 8 | #Client 9 | app.client.id=sso-auth-client 10 | app.client.secret=mySecret 11 | 12 | 13 | #MONGODB 14 | spring.data.mongodb.host=localhost 15 | spring.data.mongodb.port=27017 16 | spring.data.mongodb.database=sso-auth-server 17 | -------------------------------------------------------------------------------- /auth-server/src/main/resources/spring-security-oauth2.xml: -------------------------------------------------------------------------------- 1 | 2 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 | 64 | 65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | -------------------------------------------------------------------------------- /auth-server/src/test/java/st/malike/auth/server/service/UserServiceTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package st.malike.auth.server.service; 7 | 8 | import java.util.Arrays; 9 | import java.util.HashSet; 10 | import org.apache.commons.lang.RandomStringUtils; 11 | import static org.junit.Assert.assertNotEquals; 12 | import org.junit.Before; 13 | import org.junit.Test; 14 | import org.junit.runner.RunWith; 15 | import org.springframework.beans.factory.annotation.Autowired; 16 | import org.springframework.beans.factory.annotation.Value; 17 | import org.springframework.boot.test.IntegrationTest; 18 | import org.springframework.boot.test.SpringApplicationConfiguration; 19 | import org.springframework.security.crypto.password.PasswordEncoder; 20 | import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 21 | import org.springframework.test.context.web.WebAppConfiguration; 22 | import st.malike.auth.server.AuthServerMain; 23 | import st.malike.auth.server.model.ClientDetail; 24 | import st.malike.auth.server.model.User; 25 | import st.malike.auth.server.service.security.ClientDetailService; 26 | import uk.co.jemos.podam.api.PodamFactory; 27 | import uk.co.jemos.podam.api.PodamFactoryImpl; 28 | 29 | /** 30 | * 31 | * @author malike_st 32 | */ 33 | @RunWith(SpringJUnit4ClassRunner.class) 34 | @SpringApplicationConfiguration(classes = AuthServerMain.class) 35 | @WebAppConfiguration 36 | @IntegrationTest 37 | public class UserServiceTest { 38 | 39 | @Autowired 40 | private UserService userService; 41 | @Autowired 42 | private ClientDetailService clientDetailService; 43 | @Autowired 44 | private PasswordEncoder passwordEncoder; 45 | User awesomeUser; 46 | ClientDetail authClient; 47 | @Value("${app.client.id}") 48 | private String authClientId; 49 | @Value("${app.client.secret}") 50 | private String authClientSecret; 51 | 52 | static PodamFactory podamFactory; 53 | 54 | @Before 55 | public void setUp() { 56 | 57 | podamFactory = new PodamFactoryImpl(); 58 | userService.deleteAll(); 59 | clientDetailService.deleteAll(); 60 | 61 | // awesomeUser = podamFactory.manufacturePojo(User.class); 62 | awesomeUser = new User(); 63 | 64 | awesomeUser.setEmail("user@awesome.com"); 65 | awesomeUser.setPassword(passwordEncoder.encode("cant_hack_this")); 66 | awesomeUser.setId("thisis-awesome-1"); 67 | userService.save(awesomeUser); 68 | 69 | authClient = new ClientDetail(); 70 | authClient.setId(RandomStringUtils.randomAlphanumeric(10)); 71 | authClient.setClientId(authClientId); 72 | authClient.setResourceIds(new HashSet<>(Arrays.asList("rest_api"))); 73 | authClient.setClientSecret(passwordEncoder.encode(authClientSecret)); 74 | authClient.setRefreshTokenValiditySeconds(4500); 75 | authClient.setAccessTokenValiditySeconds(4500); 76 | authClient.setAuthorities(new HashSet<>(Arrays.asList("trust", "read", "write"))); 77 | authClient.setAuthorizedGrantTypes(new HashSet<>(Arrays.asList("client_credentials", "authorization_code", "implicit", "password", "refresh_token"))); 78 | authClient.setScope(new HashSet<>(Arrays.asList("trust", "read", "write"))); 79 | authClient.setSecretRequired(true); 80 | 81 | clientDetailService.save(authClient); 82 | 83 | } 84 | 85 | @Test 86 | public void checkingAWESOME() { 87 | assertNotEquals("awesome", "AWESOME"); 88 | } 89 | 90 | ///other chats here 91 | 92 | } 93 | -------------------------------------------------------------------------------- /client/pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | st.malike 6 | sso-auth-client 7 | 1.0-SNAPSHOT 8 | jar 9 | 10 | sso-auth-client 11 | http://maven.apache.org 12 | 13 | 14 | st.malike 15 | sso-auth 16 | 1.0-SNAPSHOT 17 | 18 | 19 | 20 | 21 | st.malike.auth.client.AuthClientMain 22 | 8 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | org.mockito 31 | mockito-all 32 | test 33 | 34 | 35 | 36 | uk.co.jemos.podam 37 | podam 38 | test 39 | jar 40 | 41 | 42 | 43 | 44 | org.springframework.boot 45 | spring-boot-starter-security 46 | 47 | 48 | 49 | 50 | org.springframework.security.oauth 51 | spring-security-oauth2 52 | 53 | 54 | 55 | org.springframework.boot 56 | spring-boot-starter-tomcat 57 | 58 | 59 | 60 | org.springframework.boot 61 | spring-boot-starter-data-mongodb 62 | 63 | 64 | 65 | 66 | org.springframework.boot 67 | spring-boot-starter-web 68 | 69 | 70 | 71 | 72 | 73 | 74 | 75 | org.springframework.boot 76 | spring-boot-maven-plugin 77 | 78 | 79 | 80 | 81 | 82 | -------------------------------------------------------------------------------- /client/src/main/java/st/malike/auth/client/AuthClientMain.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package st.malike.auth.client; 7 | 8 | import org.springframework.boot.SpringApplication; 9 | import org.springframework.boot.autoconfigure.EnableAutoConfiguration; 10 | import org.springframework.context.ApplicationContext; 11 | import org.springframework.context.annotation.ComponentScan; 12 | import org.springframework.context.annotation.Configuration; 13 | 14 | /** 15 | * 16 | * @author malike_st 17 | */ 18 | @Configuration 19 | @EnableAutoConfiguration 20 | @ComponentScan 21 | public class AuthClientMain { 22 | 23 | /** 24 | * @param args the command line arguments 25 | */ 26 | public static void main(String[] args) { 27 | // TODO code application logic here 28 | ApplicationContext context = SpringApplication.run(AuthClientMain.class, args); 29 | } 30 | 31 | } 32 | -------------------------------------------------------------------------------- /client/src/main/java/st/malike/auth/client/http/DemoController.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | package st.malike.auth.client.http; 7 | 8 | import org.springframework.stereotype.Controller; 9 | import org.springframework.web.bind.annotation.RequestMapping; 10 | import org.springframework.web.bind.annotation.ResponseBody; 11 | 12 | /** 13 | * 14 | * @author malike_st 15 | */ 16 | @Controller 17 | public class DemoController { 18 | 19 | @RequestMapping("/hello") 20 | @ResponseBody 21 | public String helloWorld() { 22 | return "Hello World."; 23 | } 24 | 25 | } 26 | -------------------------------------------------------------------------------- /client/src/main/java/st/malike/auth/client/security/ResourceServerConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | package st.malike.auth.client.security; 8 | 9 | import org.springframework.context.annotation.Configuration; 10 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 11 | import org.springframework.security.config.http.SessionCreationPolicy; 12 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; 13 | import org.springframework.security.oauth2.config.annotation.web.configuration.ResourceServerConfigurerAdapter; 14 | 15 | /** 16 | * 17 | * @author malike_st 18 | */ 19 | @Configuration 20 | @EnableResourceServer 21 | public class ResourceServerConfig extends ResourceServerConfigurerAdapter { 22 | 23 | 24 | @Override 25 | public void configure(HttpSecurity http) throws Exception { 26 | http 27 | .authorizeRequests() 28 | .antMatchers("/**").authenticated() 29 | .and() 30 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /client/src/main/java/st/malike/auth/client/security/SecurityConfig.java: -------------------------------------------------------------------------------- 1 | /* 2 | * To change this license header, choose License Headers in Project Properties. 3 | * To change this template file, choose Tools | Templates 4 | * and open the template in the editor. 5 | */ 6 | 7 | package st.malike.auth.client.security; 8 | 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.context.annotation.Bean; 11 | import org.springframework.context.annotation.Configuration; 12 | import org.springframework.security.authentication.AuthenticationManager; 13 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 14 | import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer; 15 | import org.springframework.security.oauth2.provider.authentication.OAuth2AuthenticationManager; 16 | import org.springframework.security.oauth2.provider.token.RemoteTokenServices; 17 | import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; 18 | 19 | /** 20 | * 21 | * @author malike_st 22 | */ 23 | @Configuration 24 | @EnableResourceServer 25 | public class SecurityConfig extends WebSecurityConfigurerAdapter { 26 | 27 | @Value("${app.client.id}") 28 | private String appId; 29 | @Value("${app.client.secret}") 30 | private String appSecret; 31 | @Value("${auth.server.host}") 32 | private String authServerHost; 33 | @Value("${auth.server.port}") 34 | private int authServerPort; 35 | 36 | @Bean 37 | public ResourceServerTokenServices tokenService() { 38 | RemoteTokenServices tokenServices = new RemoteTokenServices(); 39 | tokenServices.setClientId(appId); 40 | tokenServices.setClientSecret(appSecret); 41 | tokenServices.setCheckTokenEndpointUrl("http://" + authServerHost + ":" + authServerPort + "/oauth/check_token"); 42 | return tokenServices; 43 | } 44 | 45 | @Override 46 | @Bean 47 | public AuthenticationManager authenticationManagerBean() throws Exception { 48 | OAuth2AuthenticationManager authenticationManager = new OAuth2AuthenticationManager(); 49 | authenticationManager.setTokenServices(tokenService()); 50 | return authenticationManager; 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /client/src/main/resources/application.properties: -------------------------------------------------------------------------------- 1 | 2 | server.port=8081 3 | 4 | app.client.id=sso-auth-client 5 | app.client.secret=mySecret 6 | 7 | auth.server.host=localhost 8 | auth.server.port=8080 9 | 10 | -------------------------------------------------------------------------------- /hello_no_token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malike/sso-auth/21a48d2746006ab148357be6dead51cc0cc06a78/hello_no_token.png -------------------------------------------------------------------------------- /hello_token.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malike/sso-auth/21a48d2746006ab148357be6dead51cc0cc06a78/hello_token.png -------------------------------------------------------------------------------- /oauth_login.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/malike/sso-auth/21a48d2746006ab148357be6dead51cc0cc06a78/oauth_login.png -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4.0.0 4 | 5 | st.malike 6 | sso-auth 7 | 1.0-SNAPSHOT 8 | pom 9 | 10 | sso-auth 11 | http://maven.apache.org 12 | 13 | client 14 | auth-server 15 | 16 | 17 | 18 | 19 | maven 20 | mvn 21 | http://central.maven.org/maven2/ 22 | 23 | 24 | 25 | 26 | 27 | 28 | maven 29 | http://central.maven.org/maven2/ 30 | 31 | 32 | 33 | 34 | org.springframework.boot 35 | spring-boot-starter-parent 36 | 1.2.4.RELEASE 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | org.mockito 46 | mockito-all 47 | 1.9.5 48 | 49 | 50 | 51 | uk.co.jemos.podam 52 | podam 53 | 3.6.0.RELEASE 54 | jar 55 | 56 | 57 | 58 | 59 | org.springframework.boot 60 | spring-boot-starter-security 61 | 1.2.4.RELEASE 62 | 63 | 64 | 65 | 66 | org.springframework.security.oauth 67 | spring-security-oauth2 68 | 2.0.7.RELEASE 69 | 70 | 71 | 72 | org.springframework.boot 73 | spring-boot-starter-tomcat 74 | 1.2.4.RELEASE 75 | 76 | 77 | 78 | org.springframework.boot 79 | spring-boot-starter-data-mongodb 80 | 1.2.4.RELEASE 81 | 82 | 83 | 84 | 85 | org.springframework.boot 86 | spring-boot-starter-web 87 | 1.2.4.RELEASE 88 | 89 | 90 | 91 | com.google.code.gson 92 | gson 93 | 2.4 94 | 95 | 96 | 97 | commons-lang 98 | commons-lang 99 | 2.0 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 | 108 | org.springframework.boot 109 | spring-boot-maven-plugin 110 | 1.2.4.RELEASE 111 | 112 | 113 | 114 | 115 | 116 | --------------------------------------------------------------------------------