diff --git a/spring-security-oauth/.project b/spring-security-oauth/.project
new file mode 100644
index 0000000000..fe6e295165
--- /dev/null
+++ b/spring-security-oauth/.project
@@ -0,0 +1,17 @@
+
+
+ spring-security-oauth
+
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.m2e.core.maven2Nature
+
+
diff --git a/spring-security-oauth/pom.xml b/spring-security-oauth/pom.xml
new file mode 100644
index 0000000000..0531270ea4
--- /dev/null
+++ b/spring-security-oauth/pom.xml
@@ -0,0 +1,103 @@
+
+ 4.0.0
+ org.baeldung
+ spring-security-oauth
+ 1.0.0-SNAPSHOT
+
+ spring-security-oauth
+ pom
+
+
+ org.springframework.boot
+ spring-boot-starter-parent
+ 1.2.7.RELEASE
+
+
+
+ spring-security-oauth-server
+ spring-security-oauth-resource
+ spring-security-oauth-ui
+
+
+
+ spring-security-oauth
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+ ${maven-compiler-plugin.version}
+
+ 1.8
+ 1.8
+
+
+
+
+ org.apache.maven.plugins
+ maven-war-plugin
+ ${maven-war-plugin.version}
+
+ false
+
+
+
+
+ org.apache.maven.plugins
+ maven-surefire-plugin
+ ${maven-surefire-plugin.version}
+
+ true
+
+ **/*IntegrationTest.java
+ **/*LiveTest.java
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ 4.1.7.RELEASE
+ 3.2.8.RELEASE
+ 2.0.7.RELEASE
+
+
+
+ 2.5.1
+
+
+ 1.7.12
+ 1.1.3
+
+
+ 18.0
+ 3.3.2
+
+
+ 1.3
+ 4.11
+ 1.10.19
+
+ 4.4
+ 4.4
+
+ 2.4.0
+
+
+ 3.3
+ 2.6
+ 2.19
+ 1.4.16
+
+
+
+
\ No newline at end of file
diff --git a/spring-security-oauth/spring-security-oauth-resource/.classpath b/spring-security-oauth/spring-security-oauth-resource/.classpath
new file mode 100644
index 0000000000..5c3ac53820
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-resource/.classpath
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-security-oauth/spring-security-oauth-resource/.project b/spring-security-oauth/spring-security-oauth-resource/.project
new file mode 100644
index 0000000000..c3a285960b
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-resource/.project
@@ -0,0 +1,48 @@
+
+
+ spring-security-oauth-resource
+
+
+
+
+
+ org.eclipse.wst.jsdt.core.javascriptValidator
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.wst.common.project.facet.core.builder
+
+
+
+
+ org.springframework.ide.eclipse.core.springbuilder
+
+
+
+
+ org.eclipse.wst.validation.validationbuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jem.workbench.JavaEMFNature
+ org.eclipse.wst.common.modulecore.ModuleCoreNature
+ org.springframework.ide.eclipse.core.springnature
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+ org.eclipse.wst.common.project.facet.core.nature
+ org.eclipse.wst.jsdt.core.jsNature
+
+
diff --git a/spring-security-oauth/spring-security-oauth-resource/pom.xml b/spring-security-oauth/spring-security-oauth-resource/pom.xml
new file mode 100644
index 0000000000..88cc87c491
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-resource/pom.xml
@@ -0,0 +1,48 @@
+
+ 4.0.0
+ spring-security-oauth-resource
+ spring-security-oauth-resource
+ war
+
+
+ org.baeldung
+ spring-security-oauth
+ 1.0.0-SNAPSHOT
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework
+ spring-jdbc
+
+
+
+ mysql
+ mysql-connector-java
+ runtime
+
+
+
+ org.springframework.security.oauth
+ spring-security-oauth2
+ ${oauth.version}
+
+
+
+ org.apache.commons
+ commons-lang3
+ ${commons-lang3.version}
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-security-oauth/spring-security-oauth-resource/src/main/java/org/baeldung/config/OAuth2ResourceConfig.java b/spring-security-oauth/spring-security-oauth-resource/src/main/java/org/baeldung/config/OAuth2ResourceConfig.java
new file mode 100644
index 0000000000..e4f0fe5578
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-resource/src/main/java/org/baeldung/config/OAuth2ResourceConfig.java
@@ -0,0 +1,36 @@
+package org.baeldung.config;
+
+import javax.sql.DataSource;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.core.env.Environment;
+import org.springframework.jdbc.datasource.DriverManagerDataSource;
+import org.springframework.security.oauth2.provider.token.TokenStore;
+import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
+
+@Configuration
+@PropertySource({ "classpath:persistence.properties" })
+public class OAuth2ResourceConfig {
+
+ @Autowired
+ private Environment env;
+
+ @Bean
+ public DataSource dataSource() {
+ final DriverManagerDataSource dataSource = new DriverManagerDataSource();
+ dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
+ dataSource.setUrl(env.getProperty("jdbc.url"));
+ dataSource.setUsername(env.getProperty("jdbc.user"));
+ dataSource.setPassword(env.getProperty("jdbc.pass"));
+ return dataSource;
+ }
+
+ @Bean
+ public TokenStore tokenStore() {
+ return new JdbcTokenStore(dataSource());
+ }
+
+}
diff --git a/spring-security-oauth/spring-security-oauth-resource/src/main/java/org/baeldung/config/ResourceApplication.java b/spring-security-oauth/spring-security-oauth-resource/src/main/java/org/baeldung/config/ResourceApplication.java
new file mode 100644
index 0000000000..0ac197cd47
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-resource/src/main/java/org/baeldung/config/ResourceApplication.java
@@ -0,0 +1,14 @@
+package org.baeldung.config;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.web.SpringBootServletInitializer;
+
+@SpringBootApplication
+public class ResourceApplication extends SpringBootServletInitializer {
+
+ public static void main(String[] args) {
+ SpringApplication.run(ResourceApplication.class, args);
+ }
+
+}
\ No newline at end of file
diff --git a/spring-security-oauth/spring-security-oauth-resource/src/main/java/org/baeldung/config/ResourceSecurityConfig.java b/spring-security-oauth/spring-security-oauth-resource/src/main/java/org/baeldung/config/ResourceSecurityConfig.java
new file mode 100644
index 0000000000..9ad2131d1e
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-resource/src/main/java/org/baeldung/config/ResourceSecurityConfig.java
@@ -0,0 +1,17 @@
+package org.baeldung.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler;
+import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
+import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration;
+import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler;
+
+@Configuration
+@EnableGlobalMethodSecurity(prePostEnabled = true)
+public class ResourceSecurityConfig extends GlobalMethodSecurityConfiguration {
+
+ @Override
+ protected MethodSecurityExpressionHandler createExpressionHandler() {
+ return new OAuth2MethodSecurityExpressionHandler();
+ }
+}
diff --git a/spring-security-oauth/spring-security-oauth-resource/src/main/java/org/baeldung/config/ResourceWebConfig.java b/spring-security-oauth/spring-security-oauth-resource/src/main/java/org/baeldung/config/ResourceWebConfig.java
new file mode 100644
index 0000000000..ed0bb9f81b
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-resource/src/main/java/org/baeldung/config/ResourceWebConfig.java
@@ -0,0 +1,13 @@
+package org.baeldung.config;
+
+import org.springframework.context.annotation.ComponentScan;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+
+@Configuration
+@EnableWebMvc
+@ComponentScan({ "org.baeldung.web.controller" })
+public class ResourceWebConfig extends WebMvcConfigurerAdapter {
+
+}
diff --git a/spring-security-oauth/spring-security-oauth-resource/src/main/java/org/baeldung/web/controller/FooController.java b/spring-security-oauth/spring-security-oauth-resource/src/main/java/org/baeldung/web/controller/FooController.java
new file mode 100644
index 0000000000..d8f830a76c
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-resource/src/main/java/org/baeldung/web/controller/FooController.java
@@ -0,0 +1,31 @@
+package org.baeldung.web.controller;
+
+import static org.apache.commons.lang3.RandomStringUtils.randomAlphabetic;
+import static org.apache.commons.lang3.RandomStringUtils.randomNumeric;
+
+import org.baeldung.web.dto.Foo;
+import org.springframework.security.access.prepost.PreAuthorize;
+import org.springframework.security.oauth2.config.annotation.web.configuration.EnableResourceServer;
+import org.springframework.stereotype.Controller;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestMethod;
+import org.springframework.web.bind.annotation.ResponseBody;
+
+@Controller
+@EnableResourceServer
+public class FooController {
+
+ public FooController() {
+ super();
+ }
+
+ // API - read
+ @PreAuthorize("#oauth2.hasScope('read')")
+ @RequestMapping(method = RequestMethod.GET, value = "/foos/{id}")
+ @ResponseBody
+ public Foo findById(@PathVariable final long id) {
+ return new Foo(Long.parseLong(randomNumeric(2)), randomAlphabetic(4));
+ }
+
+}
diff --git a/spring-security-oauth/spring-security-oauth-resource/src/main/java/org/baeldung/web/dto/Foo.java b/spring-security-oauth/spring-security-oauth-resource/src/main/java/org/baeldung/web/dto/Foo.java
new file mode 100644
index 0000000000..9d26618e7f
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-resource/src/main/java/org/baeldung/web/dto/Foo.java
@@ -0,0 +1,36 @@
+package org.baeldung.web.dto;
+
+public class Foo {
+ private long id;
+ private String name;
+
+ public Foo() {
+ super();
+ }
+
+ public Foo(final long id, final String name) {
+ super();
+
+ this.id = id;
+ this.name = name;
+ }
+
+ //
+
+ public long getId() {
+ return id;
+ }
+
+ public void setId(final long id) {
+ this.id = id;
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public void setName(final String name) {
+ this.name = name;
+ }
+
+}
\ No newline at end of file
diff --git a/spring-security-oauth/spring-security-oauth-resource/src/main/resources/application.properties b/spring-security-oauth/spring-security-oauth-resource/src/main/resources/application.properties
new file mode 100644
index 0000000000..294f69c54e
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-resource/src/main/resources/application.properties
@@ -0,0 +1,2 @@
+#server.contextPath=/resource
+#server.port=8081
\ No newline at end of file
diff --git a/spring-security-oauth/spring-security-oauth-resource/src/main/resources/persistence.properties b/spring-security-oauth/spring-security-oauth-resource/src/main/resources/persistence.properties
new file mode 100644
index 0000000000..b975b10e9f
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-resource/src/main/resources/persistence.properties
@@ -0,0 +1,6 @@
+################### DataSource Configuration ##########################
+jdbc.driverClassName=com.mysql.jdbc.Driver
+jdbc.url=jdbc:mysql://localhost:3306/oauth2?createDatabaseIfNotExist=true
+jdbc.user=tutorialuser
+jdbc.pass=tutorialmy5ql
+
diff --git a/spring-security-oauth/spring-security-oauth-server/.classpath b/spring-security-oauth/spring-security-oauth-server/.classpath
new file mode 100644
index 0000000000..5c3ac53820
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-server/.classpath
@@ -0,0 +1,37 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-security-oauth/spring-security-oauth-server/.project b/spring-security-oauth/spring-security-oauth-server/.project
new file mode 100644
index 0000000000..a66e7f1009
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-server/.project
@@ -0,0 +1,48 @@
+
+
+ spring-security-oauth-server
+
+
+
+
+
+ org.eclipse.wst.jsdt.core.javascriptValidator
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.wst.common.project.facet.core.builder
+
+
+
+
+ org.springframework.ide.eclipse.core.springbuilder
+
+
+
+
+ org.eclipse.wst.validation.validationbuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jem.workbench.JavaEMFNature
+ org.eclipse.wst.common.modulecore.ModuleCoreNature
+ org.springframework.ide.eclipse.core.springnature
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+ org.eclipse.wst.common.project.facet.core.nature
+ org.eclipse.wst.jsdt.core.jsNature
+
+
diff --git a/spring-security-oauth/spring-security-oauth-server/pom.xml b/spring-security-oauth/spring-security-oauth-server/pom.xml
new file mode 100644
index 0000000000..211b9a240c
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-server/pom.xml
@@ -0,0 +1,57 @@
+
+ 4.0.0
+ spring-security-oauth-server
+
+ spring-security-oauth-server
+ war
+
+
+ org.baeldung
+ spring-security-oauth
+ 1.0.0-SNAPSHOT
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework
+ spring-jdbc
+
+
+
+ mysql
+ mysql-connector-java
+ runtime
+
+
+
+
+
+
+ org.springframework.security.oauth
+ spring-security-oauth2
+ ${oauth.version}
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-security-oauth/spring-security-oauth-server/src/main/java/org/baeldung/config/AuthServerOAuth2Config.java b/spring-security-oauth/spring-security-oauth-server/src/main/java/org/baeldung/config/AuthServerOAuth2Config.java
new file mode 100644
index 0000000000..572fcc5efa
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-server/src/main/java/org/baeldung/config/AuthServerOAuth2Config.java
@@ -0,0 +1,88 @@
+package org.baeldung.config;
+
+import javax.sql.DataSource;
+
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.annotation.PropertySource;
+import org.springframework.core.env.Environment;
+import org.springframework.core.io.Resource;
+import org.springframework.jdbc.datasource.DriverManagerDataSource;
+import org.springframework.jdbc.datasource.init.DataSourceInitializer;
+import org.springframework.jdbc.datasource.init.DatabasePopulator;
+import org.springframework.jdbc.datasource.init.ResourceDatabasePopulator;
+import org.springframework.security.authentication.AuthenticationManager;
+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.configuration.EnableAuthorizationServer;
+import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerEndpointsConfigurer;
+import org.springframework.security.oauth2.config.annotation.web.configurers.AuthorizationServerSecurityConfigurer;
+import org.springframework.security.oauth2.provider.token.TokenStore;
+import org.springframework.security.oauth2.provider.token.store.JdbcTokenStore;
+
+@Configuration
+@PropertySource({ "classpath:persistence.properties" })
+@EnableAuthorizationServer
+public class AuthServerOAuth2Config extends AuthorizationServerConfigurerAdapter {
+
+ @Autowired
+ private Environment env;
+
+ @Autowired
+ private AuthenticationManager authenticationManager;
+
+ @Value("classpath:schema.sql")
+ private Resource schemaScript;
+
+ @Override
+ public void configure(AuthorizationServerSecurityConfigurer oauthServer) throws Exception {
+ oauthServer.tokenKeyAccess("permitAll()").checkTokenAccess("isAuthenticated()");
+ }
+
+ @Override
+ public void configure(ClientDetailsServiceConfigurer clients) throws Exception {
+ // @formatter:off
+ clients.jdbc(dataSource())
+ .withClient("clientId")
+ .authorizedGrantTypes("implicit")
+ .scopes("read","write")
+ .autoApprove(true);
+ // @formatter:on
+ }
+
+ @Override
+ public void configure(AuthorizationServerEndpointsConfigurer endpoints) throws Exception {
+ endpoints.tokenStore(tokenStore()).authenticationManager(authenticationManager);
+ }
+
+ @Bean
+ public DataSourceInitializer dataSourceInitializer(final DataSource dataSource) {
+ final DataSourceInitializer initializer = new DataSourceInitializer();
+ initializer.setDataSource(dataSource);
+ initializer.setDatabasePopulator(databasePopulator());
+ return initializer;
+ }
+
+ private DatabasePopulator databasePopulator() {
+ final ResourceDatabasePopulator populator = new ResourceDatabasePopulator();
+ populator.addScript(schemaScript);
+ return populator;
+ }
+
+ @Bean
+ public DataSource dataSource() {
+ final DriverManagerDataSource dataSource = new DriverManagerDataSource();
+ dataSource.setDriverClassName(env.getProperty("jdbc.driverClassName"));
+ dataSource.setUrl(env.getProperty("jdbc.url"));
+ dataSource.setUsername(env.getProperty("jdbc.user"));
+ dataSource.setPassword(env.getProperty("jdbc.pass"));
+ return dataSource;
+ }
+
+ @Bean
+ public TokenStore tokenStore() {
+ return new JdbcTokenStore(dataSource());
+ }
+}
diff --git a/spring-security-oauth/spring-security-oauth-server/src/main/java/org/baeldung/config/ServerApplication.java b/spring-security-oauth/spring-security-oauth-server/src/main/java/org/baeldung/config/ServerApplication.java
new file mode 100644
index 0000000000..4b1ff2c7ad
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-server/src/main/java/org/baeldung/config/ServerApplication.java
@@ -0,0 +1,14 @@
+package org.baeldung.config;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.web.SpringBootServletInitializer;
+
+@SpringBootApplication
+public class ServerApplication extends SpringBootServletInitializer {
+
+ public static void main(String[] args) {
+ SpringApplication.run(ServerApplication.class, args);
+ }
+
+}
\ No newline at end of file
diff --git a/spring-security-oauth/spring-security-oauth-server/src/main/java/org/baeldung/config/ServerSecurityConfig.java b/spring-security-oauth/spring-security-oauth-server/src/main/java/org/baeldung/config/ServerSecurityConfig.java
new file mode 100644
index 0000000000..ec9bdfe0ce
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-server/src/main/java/org/baeldung/config/ServerSecurityConfig.java
@@ -0,0 +1,27 @@
+package org.baeldung.config;
+
+import org.springframework.context.annotation.Configuration;
+import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
+import org.springframework.security.config.annotation.web.builders.HttpSecurity;
+import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+
+@Configuration
+public class ServerSecurityConfig extends WebSecurityConfigurerAdapter {
+
+ @Override
+ protected void configure(final AuthenticationManagerBuilder auth) throws Exception {
+ auth.inMemoryAuthentication().withUser("john").password("123").roles("USER");
+ }
+
+ @Override
+ protected void configure(HttpSecurity http) throws Exception {
+ // @formatter:off
+ http
+ .authorizeRequests()
+ .antMatchers("/login").permitAll()
+ .anyRequest().authenticated()
+ .and()
+ .formLogin().permitAll();
+ // @formatter:on
+ }
+}
diff --git a/spring-security-oauth/spring-security-oauth-server/src/main/resources/application.properties b/spring-security-oauth/spring-security-oauth-server/src/main/resources/application.properties
new file mode 100644
index 0000000000..b7465f46b4
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-server/src/main/resources/application.properties
@@ -0,0 +1 @@
+#server.contextPath=/server
\ No newline at end of file
diff --git a/spring-security-oauth/spring-security-oauth-server/src/main/resources/persistence.properties b/spring-security-oauth/spring-security-oauth-server/src/main/resources/persistence.properties
new file mode 100644
index 0000000000..b975b10e9f
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-server/src/main/resources/persistence.properties
@@ -0,0 +1,6 @@
+################### DataSource Configuration ##########################
+jdbc.driverClassName=com.mysql.jdbc.Driver
+jdbc.url=jdbc:mysql://localhost:3306/oauth2?createDatabaseIfNotExist=true
+jdbc.user=tutorialuser
+jdbc.pass=tutorialmy5ql
+
diff --git a/spring-security-oauth/spring-security-oauth-server/src/main/resources/schema.sql b/spring-security-oauth/spring-security-oauth-server/src/main/resources/schema.sql
new file mode 100644
index 0000000000..02c4d824b7
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-server/src/main/resources/schema.sql
@@ -0,0 +1,66 @@
+-- used in tests that use HSQL
+create table if not exists oauth_client_details (
+ client_id VARCHAR(256) PRIMARY KEY,
+ resource_ids VARCHAR(256),
+ client_secret VARCHAR(256),
+ scope VARCHAR(256),
+ authorized_grant_types VARCHAR(256),
+ web_server_redirect_uri VARCHAR(256),
+ authorities VARCHAR(256),
+ access_token_validity INTEGER,
+ refresh_token_validity INTEGER,
+ additional_information VARCHAR(4096),
+ autoapprove VARCHAR(256)
+);
+
+create table if not exists oauth_client_token (
+ token_id VARCHAR(256),
+ token LONG VARBINARY,
+ authentication_id VARCHAR(256) PRIMARY KEY,
+ user_name VARCHAR(256),
+ client_id VARCHAR(256)
+);
+
+create table if not exists oauth_access_token (
+ token_id VARCHAR(256),
+ token LONG VARBINARY,
+ authentication_id VARCHAR(256) PRIMARY KEY,
+ user_name VARCHAR(256),
+ client_id VARCHAR(256),
+ authentication LONG VARBINARY,
+ refresh_token VARCHAR(256)
+);
+
+create table if not exists oauth_refresh_token (
+ token_id VARCHAR(256),
+ token LONG VARBINARY,
+ authentication LONG VARBINARY
+);
+
+create table if not exists oauth_code (
+ code VARCHAR(256), authentication LONG VARBINARY
+);
+
+create table if not exists oauth_approvals (
+ userId VARCHAR(256),
+ clientId VARCHAR(256),
+ scope VARCHAR(256),
+ status VARCHAR(10),
+ expiresAt TIMESTAMP,
+ lastModifiedAt TIMESTAMP
+);
+
+-- customized oauth_client_details table
+create table if not exists ClientDetails (
+ appId VARCHAR(256) PRIMARY KEY,
+ resourceIds VARCHAR(256),
+ appSecret VARCHAR(256),
+ scope VARCHAR(256),
+ grantTypes VARCHAR(256),
+ redirectUrl VARCHAR(256),
+ authorities VARCHAR(256),
+ access_token_validity INTEGER,
+ refresh_token_validity INTEGER,
+ additionalInformation VARCHAR(4096),
+ autoApproveScopes VARCHAR(256)
+);
diff --git a/spring-security-oauth/spring-security-oauth-ui/.classpath b/spring-security-oauth/spring-security-oauth-ui/.classpath
new file mode 100644
index 0000000000..0cad5db2d0
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-ui/.classpath
@@ -0,0 +1,32 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/spring-security-oauth/spring-security-oauth-ui/.project b/spring-security-oauth/spring-security-oauth-ui/.project
new file mode 100644
index 0000000000..7ff5398a88
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-ui/.project
@@ -0,0 +1,48 @@
+
+
+ spring-security-oauth-ui
+
+
+
+
+
+ org.eclipse.wst.jsdt.core.javascriptValidator
+
+
+
+
+ org.eclipse.jdt.core.javabuilder
+
+
+
+
+ org.eclipse.wst.common.project.facet.core.builder
+
+
+
+
+ org.springframework.ide.eclipse.core.springbuilder
+
+
+
+
+ org.eclipse.wst.validation.validationbuilder
+
+
+
+
+ org.eclipse.m2e.core.maven2Builder
+
+
+
+
+
+ org.eclipse.jem.workbench.JavaEMFNature
+ org.eclipse.wst.common.modulecore.ModuleCoreNature
+ org.springframework.ide.eclipse.core.springnature
+ org.eclipse.jdt.core.javanature
+ org.eclipse.m2e.core.maven2Nature
+ org.eclipse.wst.common.project.facet.core.nature
+ org.eclipse.wst.jsdt.core.jsNature
+
+
diff --git a/spring-security-oauth/spring-security-oauth-ui/pom.xml b/spring-security-oauth/spring-security-oauth-ui/pom.xml
new file mode 100644
index 0000000000..a9b25f75c8
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-ui/pom.xml
@@ -0,0 +1,29 @@
+
+ 4.0.0
+ spring-security-oauth-ui
+
+ spring-security-oauth-ui
+ war
+
+
+ org.baeldung
+ spring-security-oauth
+ 1.0.0-SNAPSHOT
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-starter-web
+
+
+
+ org.springframework.boot
+ spring-boot-starter-thymeleaf
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-security-oauth/spring-security-oauth-ui/src/main/java/org/baeldung/config/UiApplication.java b/spring-security-oauth/spring-security-oauth-ui/src/main/java/org/baeldung/config/UiApplication.java
new file mode 100644
index 0000000000..8f491516aa
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-ui/src/main/java/org/baeldung/config/UiApplication.java
@@ -0,0 +1,13 @@
+package org.baeldung.config;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.web.SpringBootServletInitializer;
+
+@SpringBootApplication
+public class UiApplication extends SpringBootServletInitializer {
+
+ public static void main(String[] args) {
+ SpringApplication.run(UiApplication.class, args);
+ }
+}
\ No newline at end of file
diff --git a/spring-security-oauth/spring-security-oauth-ui/src/main/java/org/baeldung/config/UiWebConfig.java b/spring-security-oauth/spring-security-oauth-ui/src/main/java/org/baeldung/config/UiWebConfig.java
new file mode 100644
index 0000000000..2ee0585615
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-ui/src/main/java/org/baeldung/config/UiWebConfig.java
@@ -0,0 +1,38 @@
+package org.baeldung.config;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+import org.springframework.context.support.PropertySourcesPlaceholderConfigurer;
+import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer;
+import org.springframework.web.servlet.config.annotation.EnableWebMvc;
+import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
+import org.springframework.web.servlet.config.annotation.ViewControllerRegistry;
+import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter;
+
+@Configuration
+@EnableWebMvc
+public class UiWebConfig extends WebMvcConfigurerAdapter {
+
+ @Bean
+ public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
+ return new PropertySourcesPlaceholderConfigurer();
+ }
+
+ @Override
+ public void configureDefaultServletHandling(final DefaultServletHandlerConfigurer configurer) {
+ configurer.enable();
+ }
+
+ @Override
+ public void addViewControllers(final ViewControllerRegistry registry) {
+ super.addViewControllers(registry);
+ registry.addViewController("/index");
+ registry.addViewController("/oauthTemp");
+ }
+
+ @Override
+ public void addResourceHandlers(final ResourceHandlerRegistry registry) {
+ registry.addResourceHandler("/resources/**").addResourceLocations("/resources/");
+ }
+
+}
\ No newline at end of file
diff --git a/spring-security-oauth/spring-security-oauth-ui/src/main/resources/application.properties b/spring-security-oauth/spring-security-oauth-ui/src/main/resources/application.properties
new file mode 100644
index 0000000000..84ffdbb9ad
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-ui/src/main/resources/application.properties
@@ -0,0 +1,2 @@
+#server.context-path=/ui
+#server.port=8080
\ No newline at end of file
diff --git a/spring-security-oauth/spring-security-oauth-ui/src/main/resources/oauth-ng.js b/spring-security-oauth/spring-security-oauth-ui/src/main/resources/oauth-ng.js
new file mode 100644
index 0000000000..333070b935
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-ui/src/main/resources/oauth-ng.js
@@ -0,0 +1,539 @@
+/* oauth-ng - v0.4.2 - 2015-08-27 */
+
+'use strict';
+
+// App libraries
+angular.module('oauth', [
+ 'oauth.directive', // login directive
+ 'oauth.accessToken', // access token service
+ 'oauth.endpoint', // oauth endpoint service
+ 'oauth.profile', // profile model
+ 'oauth.storage', // storage
+ 'oauth.interceptor', // bearer token interceptor
+ 'oauth.configuration' // token appender
+])
+ .config(['$locationProvider','$httpProvider',
+ function($locationProvider, $httpProvider) {
+ $httpProvider.interceptors.push('ExpiredInterceptor');
+ }]);
+
+'use strict';
+
+var accessTokenService = angular.module('oauth.accessToken', []);
+
+accessTokenService.factory('AccessToken', ['Storage', '$rootScope', '$location', '$interval', function(Storage, $rootScope, $location, $interval){
+
+ var service = {
+ token: null
+ },
+ oAuth2HashTokens = [ //per http://tools.ietf.org/html/rfc6749#section-4.2.2
+ 'access_token', 'token_type', 'expires_in', 'scope', 'state',
+ 'error','error_description'
+ ];
+
+ /**
+ * Returns the access token.
+ */
+ service.get = function(){
+ return this.token;
+ };
+
+ /**
+ * Sets and returns the access token. It tries (in order) the following strategies:
+ * - takes the token from the fragment URI
+ * - takes the token from the sessionStorage
+ */
+ service.set = function(){
+ this.setTokenFromString($location.hash());
+
+ //If hash is present in URL always use it, cuz its coming from oAuth2 provider redirect
+ if(null === service.token){
+ setTokenFromSession();
+ }
+
+ return this.token;
+ };
+
+ /**
+ * Delete the access token and remove the session.
+ * @returns {null}
+ */
+ service.destroy = function(){
+ Storage.delete('token');
+ this.token = null;
+ return this.token;
+ };
+
+ /**
+ * Tells if the access token is expired.
+ */
+ service.expired = function(){
+ return (this.token && this.token.expires_at && new Date(this.token.expires_at) < new Date());
+ };
+
+ /**
+ * Get the access token from a string and save it
+ * @param hash
+ */
+ service.setTokenFromString = function(hash){
+ var params = getTokenFromString(hash);
+
+ if(params){
+ removeFragment();
+ setToken(params);
+ setExpiresAt();
+ // We have to save it again to make sure expires_at is set
+ // and the expiry event is set up properly
+ setToken(this.token);
+ $rootScope.$broadcast('oauth:login', service.token);
+ }
+ };
+
+ /* * * * * * * * * *
+ * PRIVATE METHODS *
+ * * * * * * * * * */
+
+ /**
+ * Set the access token from the sessionStorage.
+ */
+ var setTokenFromSession = function(){
+ var params = Storage.get('token');
+ if (params) {
+ setToken(params);
+ }
+ };
+
+ /**
+ * Set the access token.
+ *
+ * @param params
+ * @returns {*|{}}
+ */
+ var setToken = function(params){
+ service.token = service.token || {}; // init the token
+ angular.extend(service.token, params); // set the access token params
+ setTokenInSession(); // save the token into the session
+ setExpiresAtEvent(); // event to fire when the token expires
+
+ return service.token;
+ };
+
+ /**
+ * Parse the fragment URI and return an object
+ * @param hash
+ * @returns {{}}
+ */
+ var getTokenFromString = function(hash){
+ var params = {},
+ regex = /([^&=]+)=([^&]*)/g,
+ m;
+
+ while ((m = regex.exec(hash)) !== null) {
+ params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
+ }
+
+ if(params.access_token || params.error){
+ return params;
+ }
+ };
+
+ /**
+ * Save the access token into the session
+ */
+ var setTokenInSession = function(){
+ Storage.set('token', service.token);
+ };
+
+ /**
+ * Set the access token expiration date (useful for refresh logics)
+ */
+ var setExpiresAt = function(){
+ if (!service.token) {
+ return;
+ }
+ if(typeof(service.token.expires_in) !== 'undefined' && service.token.expires_in !== null) {
+ var expires_at = new Date();
+ expires_at.setSeconds(expires_at.getSeconds() + parseInt(service.token.expires_in)-60); // 60 seconds less to secure browser and response latency
+ service.token.expires_at = expires_at;
+ }
+ else {
+ service.token.expires_at = null;
+ }
+ };
+
+
+ /**
+ * Set the timeout at which the expired event is fired
+ */
+ var setExpiresAtEvent = function(){
+ // Don't bother if there's no expires token
+ if (typeof(service.token.expires_at) === 'undefined' || service.token.expires_at === null) {
+ return;
+ }
+ var time = (new Date(service.token.expires_at))-(new Date());
+ if(time && time > 0){
+ $interval(function(){
+ $rootScope.$broadcast('oauth:expired', service.token);
+ }, time, 1);
+ }
+ };
+
+ /**
+ * Remove the oAuth2 pieces from the hash fragment
+ */
+ var removeFragment = function(){
+ var curHash = $location.hash();
+ angular.forEach(oAuth2HashTokens,function(hashKey){
+ var re = new RegExp('&'+hashKey+'(=[^&]*)?|^'+hashKey+'(=[^&]*)?&?');
+ curHash = curHash.replace(re,'');
+ });
+
+ $location.hash(curHash);
+ };
+
+ return service;
+
+}]);
+
+'use strict';
+
+var endpointClient = angular.module('oauth.endpoint', []);
+
+endpointClient.factory('Endpoint', function() {
+
+ var service = {};
+
+ /*
+ * Defines the authorization URL
+ */
+
+ service.set = function(configuration) {
+ this.config = configuration;
+ return this.get();
+ };
+
+ /*
+ * Returns the authorization URL
+ */
+
+ service.get = function( overrides ) {
+ var params = angular.extend( {}, service.config, overrides);
+ var oAuthScope = (params.scope) ? encodeURIComponent(params.scope) : '',
+ state = (params.state) ? encodeURIComponent(params.state) : '',
+ authPathHasQuery = (params.authorizePath.indexOf('?') === -1) ? false : true,
+ appendChar = (authPathHasQuery) ? '&' : '?', //if authorizePath has ? already append OAuth2 params
+ responseType = (params.responseType) ? encodeURIComponent(params.responseType) : '';
+
+ var url = params.site +
+ params.authorizePath +
+ appendChar + 'response_type=' + responseType + '&' +
+ 'client_id=' + encodeURIComponent(params.clientId) + '&' +
+ 'redirect_uri=' + encodeURIComponent(params.redirectUri) + '&' +
+ 'scope=' + oAuthScope + '&' +
+ 'state=' + state;
+
+ if( params.nonce ) {
+ url = url + '&nonce=' + params.nonce;
+ }
+ return url;
+ };
+
+ /*
+ * Redirects the app to the authorization URL
+ */
+
+ service.redirect = function( overrides ) {
+ var targetLocation = this.get( overrides );
+ window.location.replace(targetLocation);
+ };
+
+ return service;
+});
+
+'use strict';
+
+var profileClient = angular.module('oauth.profile', []);
+
+profileClient.factory('Profile', ['$http', 'AccessToken', '$rootScope', function($http, AccessToken, $rootScope) {
+ var service = {};
+ var profile;
+
+ service.find = function(uri) {
+ var promise = $http.get(uri, { headers: headers() });
+ promise.success(function(response) {
+ profile = response;
+ $rootScope.$broadcast('oauth:profile', profile);
+ });
+ return promise;
+ };
+
+ service.get = function() {
+ return profile;
+ };
+
+ service.set = function(resource) {
+ profile = resource;
+ return profile;
+ };
+
+ var headers = function() {
+ return { Authorization: 'Bearer ' + AccessToken.get().access_token };
+ };
+
+ return service;
+}]);
+
+'use strict';
+
+var storageService = angular.module('oauth.storage', ['ngStorage']);
+
+storageService.factory('Storage', ['$rootScope', '$sessionStorage', '$localStorage', function($rootScope, $sessionStorage, $localStorage){
+
+ var service = {
+ storage: $sessionStorage // By default
+ };
+
+ /**
+ * Deletes the item from storage,
+ * Returns the item's previous value
+ */
+ service.delete = function (name) {
+ var stored = this.get(name);
+ delete this.storage[name];
+ return stored;
+ };
+
+ /**
+ * Returns the item from storage
+ */
+ service.get = function (name) {
+ return this.storage[name];
+ };
+
+ /**
+ * Sets the item in storage to the value specified
+ * Returns the item's value
+ */
+ service.set = function (name, value) {
+ this.storage[name] = value;
+ return this.get(name);
+ };
+
+ /**
+ * Change the storage service being used
+ */
+ service.use = function (storage) {
+ if (storage === 'sessionStorage') {
+ this.storage = $sessionStorage;
+ } else if (storage === 'localStorage') {
+ this.storage = $localStorage;
+ }
+ };
+
+ return service;
+}]);
+'use strict';
+
+var oauthConfigurationService = angular.module('oauth.configuration', []);
+
+oauthConfigurationService.provider('OAuthConfiguration', function() {
+ var _config = {};
+
+ this.init = function(config, httpProvider) {
+ _config.protectedResources = config.protectedResources || [];
+ httpProvider.interceptors.push('AuthInterceptor');
+ };
+
+ this.$get = function() {
+ return {
+ getConfig: function() {
+ return _config;
+ }
+ };
+ };
+})
+.factory('AuthInterceptor', function($q, $rootScope, OAuthConfiguration, AccessToken) {
+ return {
+ 'request': function(config) {
+ OAuthConfiguration.getConfig().protectedResources.forEach(function(resource) {
+ // If the url is one of the protected resources, we want to see if there's a token and then
+ // add the token if it exists.
+ if (config.url.indexOf(resource) > -1) {
+ var token = AccessToken.get();
+ if (token) {
+ config.headers.Authorization = 'Bearer ' + token.access_token;
+ }
+ }
+ });
+
+ return config;
+ }
+ };
+});
+'use strict';
+
+var interceptorService = angular.module('oauth.interceptor', []);
+
+interceptorService.factory('ExpiredInterceptor', ['Storage', '$rootScope', function (Storage, $rootScope) {
+
+ var service = {};
+
+ service.request = function(config) {
+ var token = Storage.get('token');
+
+ if (token && expired(token)) {
+ $rootScope.$broadcast('oauth:expired', token);
+ }
+
+ return config;
+ };
+
+ var expired = function(token) {
+ return (token && token.expires_at && new Date(token.expires_at) < new Date());
+ };
+
+ return service;
+}]);
+
+'use strict';
+
+var directives = angular.module('oauth.directive', []);
+
+directives.directive('oauth', [
+ 'AccessToken',
+ 'Endpoint',
+ 'Profile',
+ 'Storage',
+ '$location',
+ '$rootScope',
+ '$compile',
+ '$http',
+ '$templateCache',
+ function(AccessToken, Endpoint, Profile, Storage, $location, $rootScope, $compile, $http, $templateCache) {
+
+ var definition = {
+ restrict: 'AE',
+ replace: true,
+ scope: {
+ site: '@', // (required) set the oauth server host (e.g. http://oauth.example.com)
+ clientId: '@', // (required) client id
+ redirectUri: '@', // (required) client redirect uri
+ responseType: '@', // (optional) response type, defaults to token (use 'token' for implicit flow and 'code' for authorization code flow
+ scope: '@', // (optional) scope
+ profileUri: '@', // (optional) user profile uri (e.g http://example.com/me)
+ template: '@', // (optional) template to render (e.g bower_components/oauth-ng/dist/views/templates/default.html)
+ text: '@', // (optional) login text
+ authorizePath: '@', // (optional) authorization url
+ state: '@', // (optional) An arbitrary unique string created by your app to guard against Cross-site Request Forgery
+ storage: '@' // (optional) Store token in 'sessionStorage' or 'localStorage', defaults to 'sessionStorage'
+ }
+ };
+
+ definition.link = function postLink(scope, element) {
+ scope.show = 'none';
+
+ scope.$watch('clientId', function() {
+ init();
+ });
+
+ var init = function() {
+ initAttributes(); // sets defaults
+ Storage.use(scope.storage);// set storage
+ compile(); // compiles the desired layout
+ Endpoint.set(scope); // sets the oauth authorization url
+ AccessToken.set(scope); // sets the access token object (if existing, from fragment or session)
+ initProfile(scope); // gets the profile resource (if existing the access token)
+ initView(); // sets the view (logged in or out)
+ };
+
+ var initAttributes = function() {
+ scope.authorizePath = scope.authorizePath || '/oauth/authorize';
+ scope.tokenPath = scope.tokenPath || '/oauth/token';
+ scope.template = scope.template || 'bower_components/oauth-ng/dist/views/templates/default.html';
+ scope.responseType = scope.responseType || 'token';
+ scope.text = scope.text || 'Sign In';
+ scope.state = scope.state || undefined;
+ scope.scope = scope.scope || undefined;
+ scope.storage = scope.storage || 'sessionStorage';
+ };
+
+ var compile = function() {
+ $http.get(scope.template, { cache: $templateCache }).success(function(html) {
+ element.html(html);
+ $compile(element.contents())(scope);
+ });
+ };
+
+ var initProfile = function(scope) {
+ var token = AccessToken.get();
+
+ if (token && token.access_token && scope.profileUri) {
+ Profile.find(scope.profileUri).success(function(response) {
+ scope.profile = response;
+ });
+ }
+ };
+
+ var initView = function() {
+ var token = AccessToken.get();
+
+ if (!token) {
+ return loggedOut(); // without access token it's logged out
+ }
+ if (token.access_token) {
+ return authorized(); // if there is the access token we are done
+ }
+ if (token.error) {
+ return denied(); // if the request has been denied we fire the denied event
+ }
+ };
+
+ scope.login = function() {
+ Endpoint.redirect();
+ };
+
+ scope.logout = function() {
+ AccessToken.destroy(scope);
+ $rootScope.$broadcast('oauth:logout');
+ loggedOut();
+ };
+
+ scope.$on('oauth:expired', function() {
+ AccessToken.destroy(scope);
+ scope.show = 'logged-out';
+ });
+
+ // user is authorized
+ var authorized = function() {
+ $rootScope.$broadcast('oauth:authorized', AccessToken.get());
+ scope.show = 'logged-in';
+ };
+
+ // set the oauth directive to the logged-out status
+ var loggedOut = function() {
+ $rootScope.$broadcast('oauth:loggedOut');
+ scope.show = 'logged-out';
+ };
+
+ // set the oauth directive to the denied status
+ var denied = function() {
+ scope.show = 'denied';
+ $rootScope.$broadcast('oauth:denied');
+ };
+
+ // Updates the template at runtime
+ scope.$on('oauth:template:update', function(event, template) {
+ scope.template = template;
+ compile(scope);
+ });
+
+ // Hack to update the directive content on logout
+ // TODO think to a cleaner solution
+ scope.$on('$routeChangeSuccess', function () {
+ init();
+ });
+ };
+
+ return definition;
+ }
+]);
diff --git a/spring-security-oauth/spring-security-oauth-ui/src/main/resources/templates/header.html b/spring-security-oauth/spring-security-oauth-ui/src/main/resources/templates/header.html
new file mode 100644
index 0000000000..db0cb5b6d3
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-ui/src/main/resources/templates/header.html
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-security-oauth/spring-security-oauth-ui/src/main/resources/templates/index.html b/spring-security-oauth/spring-security-oauth-ui/src/main/resources/templates/index.html
new file mode 100755
index 0000000000..e2458c2940
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-ui/src/main/resources/templates/index.html
@@ -0,0 +1,30 @@
+
+
+
+
+Spring Security OAuth
+
+
+
+
+
+
+
+
Foo Details
+
+ ID
+ {{foo.id}}
+
+
+
+Name
+{{foo.name}}
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/spring-security-oauth/spring-security-oauth-ui/src/main/resources/templates/oauthTemp.html b/spring-security-oauth/spring-security-oauth-ui/src/main/resources/templates/oauthTemp.html
new file mode 100644
index 0000000000..1efc1eed3c
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-ui/src/main/resources/templates/oauthTemp.html
@@ -0,0 +1,6 @@
+
\ No newline at end of file
diff --git a/spring-security-oauth/spring-security-oauth-ui/src/main/webapp/resources/oauth-ng.js b/spring-security-oauth/spring-security-oauth-ui/src/main/webapp/resources/oauth-ng.js
new file mode 100644
index 0000000000..333070b935
--- /dev/null
+++ b/spring-security-oauth/spring-security-oauth-ui/src/main/webapp/resources/oauth-ng.js
@@ -0,0 +1,539 @@
+/* oauth-ng - v0.4.2 - 2015-08-27 */
+
+'use strict';
+
+// App libraries
+angular.module('oauth', [
+ 'oauth.directive', // login directive
+ 'oauth.accessToken', // access token service
+ 'oauth.endpoint', // oauth endpoint service
+ 'oauth.profile', // profile model
+ 'oauth.storage', // storage
+ 'oauth.interceptor', // bearer token interceptor
+ 'oauth.configuration' // token appender
+])
+ .config(['$locationProvider','$httpProvider',
+ function($locationProvider, $httpProvider) {
+ $httpProvider.interceptors.push('ExpiredInterceptor');
+ }]);
+
+'use strict';
+
+var accessTokenService = angular.module('oauth.accessToken', []);
+
+accessTokenService.factory('AccessToken', ['Storage', '$rootScope', '$location', '$interval', function(Storage, $rootScope, $location, $interval){
+
+ var service = {
+ token: null
+ },
+ oAuth2HashTokens = [ //per http://tools.ietf.org/html/rfc6749#section-4.2.2
+ 'access_token', 'token_type', 'expires_in', 'scope', 'state',
+ 'error','error_description'
+ ];
+
+ /**
+ * Returns the access token.
+ */
+ service.get = function(){
+ return this.token;
+ };
+
+ /**
+ * Sets and returns the access token. It tries (in order) the following strategies:
+ * - takes the token from the fragment URI
+ * - takes the token from the sessionStorage
+ */
+ service.set = function(){
+ this.setTokenFromString($location.hash());
+
+ //If hash is present in URL always use it, cuz its coming from oAuth2 provider redirect
+ if(null === service.token){
+ setTokenFromSession();
+ }
+
+ return this.token;
+ };
+
+ /**
+ * Delete the access token and remove the session.
+ * @returns {null}
+ */
+ service.destroy = function(){
+ Storage.delete('token');
+ this.token = null;
+ return this.token;
+ };
+
+ /**
+ * Tells if the access token is expired.
+ */
+ service.expired = function(){
+ return (this.token && this.token.expires_at && new Date(this.token.expires_at) < new Date());
+ };
+
+ /**
+ * Get the access token from a string and save it
+ * @param hash
+ */
+ service.setTokenFromString = function(hash){
+ var params = getTokenFromString(hash);
+
+ if(params){
+ removeFragment();
+ setToken(params);
+ setExpiresAt();
+ // We have to save it again to make sure expires_at is set
+ // and the expiry event is set up properly
+ setToken(this.token);
+ $rootScope.$broadcast('oauth:login', service.token);
+ }
+ };
+
+ /* * * * * * * * * *
+ * PRIVATE METHODS *
+ * * * * * * * * * */
+
+ /**
+ * Set the access token from the sessionStorage.
+ */
+ var setTokenFromSession = function(){
+ var params = Storage.get('token');
+ if (params) {
+ setToken(params);
+ }
+ };
+
+ /**
+ * Set the access token.
+ *
+ * @param params
+ * @returns {*|{}}
+ */
+ var setToken = function(params){
+ service.token = service.token || {}; // init the token
+ angular.extend(service.token, params); // set the access token params
+ setTokenInSession(); // save the token into the session
+ setExpiresAtEvent(); // event to fire when the token expires
+
+ return service.token;
+ };
+
+ /**
+ * Parse the fragment URI and return an object
+ * @param hash
+ * @returns {{}}
+ */
+ var getTokenFromString = function(hash){
+ var params = {},
+ regex = /([^&=]+)=([^&]*)/g,
+ m;
+
+ while ((m = regex.exec(hash)) !== null) {
+ params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
+ }
+
+ if(params.access_token || params.error){
+ return params;
+ }
+ };
+
+ /**
+ * Save the access token into the session
+ */
+ var setTokenInSession = function(){
+ Storage.set('token', service.token);
+ };
+
+ /**
+ * Set the access token expiration date (useful for refresh logics)
+ */
+ var setExpiresAt = function(){
+ if (!service.token) {
+ return;
+ }
+ if(typeof(service.token.expires_in) !== 'undefined' && service.token.expires_in !== null) {
+ var expires_at = new Date();
+ expires_at.setSeconds(expires_at.getSeconds() + parseInt(service.token.expires_in)-60); // 60 seconds less to secure browser and response latency
+ service.token.expires_at = expires_at;
+ }
+ else {
+ service.token.expires_at = null;
+ }
+ };
+
+
+ /**
+ * Set the timeout at which the expired event is fired
+ */
+ var setExpiresAtEvent = function(){
+ // Don't bother if there's no expires token
+ if (typeof(service.token.expires_at) === 'undefined' || service.token.expires_at === null) {
+ return;
+ }
+ var time = (new Date(service.token.expires_at))-(new Date());
+ if(time && time > 0){
+ $interval(function(){
+ $rootScope.$broadcast('oauth:expired', service.token);
+ }, time, 1);
+ }
+ };
+
+ /**
+ * Remove the oAuth2 pieces from the hash fragment
+ */
+ var removeFragment = function(){
+ var curHash = $location.hash();
+ angular.forEach(oAuth2HashTokens,function(hashKey){
+ var re = new RegExp('&'+hashKey+'(=[^&]*)?|^'+hashKey+'(=[^&]*)?&?');
+ curHash = curHash.replace(re,'');
+ });
+
+ $location.hash(curHash);
+ };
+
+ return service;
+
+}]);
+
+'use strict';
+
+var endpointClient = angular.module('oauth.endpoint', []);
+
+endpointClient.factory('Endpoint', function() {
+
+ var service = {};
+
+ /*
+ * Defines the authorization URL
+ */
+
+ service.set = function(configuration) {
+ this.config = configuration;
+ return this.get();
+ };
+
+ /*
+ * Returns the authorization URL
+ */
+
+ service.get = function( overrides ) {
+ var params = angular.extend( {}, service.config, overrides);
+ var oAuthScope = (params.scope) ? encodeURIComponent(params.scope) : '',
+ state = (params.state) ? encodeURIComponent(params.state) : '',
+ authPathHasQuery = (params.authorizePath.indexOf('?') === -1) ? false : true,
+ appendChar = (authPathHasQuery) ? '&' : '?', //if authorizePath has ? already append OAuth2 params
+ responseType = (params.responseType) ? encodeURIComponent(params.responseType) : '';
+
+ var url = params.site +
+ params.authorizePath +
+ appendChar + 'response_type=' + responseType + '&' +
+ 'client_id=' + encodeURIComponent(params.clientId) + '&' +
+ 'redirect_uri=' + encodeURIComponent(params.redirectUri) + '&' +
+ 'scope=' + oAuthScope + '&' +
+ 'state=' + state;
+
+ if( params.nonce ) {
+ url = url + '&nonce=' + params.nonce;
+ }
+ return url;
+ };
+
+ /*
+ * Redirects the app to the authorization URL
+ */
+
+ service.redirect = function( overrides ) {
+ var targetLocation = this.get( overrides );
+ window.location.replace(targetLocation);
+ };
+
+ return service;
+});
+
+'use strict';
+
+var profileClient = angular.module('oauth.profile', []);
+
+profileClient.factory('Profile', ['$http', 'AccessToken', '$rootScope', function($http, AccessToken, $rootScope) {
+ var service = {};
+ var profile;
+
+ service.find = function(uri) {
+ var promise = $http.get(uri, { headers: headers() });
+ promise.success(function(response) {
+ profile = response;
+ $rootScope.$broadcast('oauth:profile', profile);
+ });
+ return promise;
+ };
+
+ service.get = function() {
+ return profile;
+ };
+
+ service.set = function(resource) {
+ profile = resource;
+ return profile;
+ };
+
+ var headers = function() {
+ return { Authorization: 'Bearer ' + AccessToken.get().access_token };
+ };
+
+ return service;
+}]);
+
+'use strict';
+
+var storageService = angular.module('oauth.storage', ['ngStorage']);
+
+storageService.factory('Storage', ['$rootScope', '$sessionStorage', '$localStorage', function($rootScope, $sessionStorage, $localStorage){
+
+ var service = {
+ storage: $sessionStorage // By default
+ };
+
+ /**
+ * Deletes the item from storage,
+ * Returns the item's previous value
+ */
+ service.delete = function (name) {
+ var stored = this.get(name);
+ delete this.storage[name];
+ return stored;
+ };
+
+ /**
+ * Returns the item from storage
+ */
+ service.get = function (name) {
+ return this.storage[name];
+ };
+
+ /**
+ * Sets the item in storage to the value specified
+ * Returns the item's value
+ */
+ service.set = function (name, value) {
+ this.storage[name] = value;
+ return this.get(name);
+ };
+
+ /**
+ * Change the storage service being used
+ */
+ service.use = function (storage) {
+ if (storage === 'sessionStorage') {
+ this.storage = $sessionStorage;
+ } else if (storage === 'localStorage') {
+ this.storage = $localStorage;
+ }
+ };
+
+ return service;
+}]);
+'use strict';
+
+var oauthConfigurationService = angular.module('oauth.configuration', []);
+
+oauthConfigurationService.provider('OAuthConfiguration', function() {
+ var _config = {};
+
+ this.init = function(config, httpProvider) {
+ _config.protectedResources = config.protectedResources || [];
+ httpProvider.interceptors.push('AuthInterceptor');
+ };
+
+ this.$get = function() {
+ return {
+ getConfig: function() {
+ return _config;
+ }
+ };
+ };
+})
+.factory('AuthInterceptor', function($q, $rootScope, OAuthConfiguration, AccessToken) {
+ return {
+ 'request': function(config) {
+ OAuthConfiguration.getConfig().protectedResources.forEach(function(resource) {
+ // If the url is one of the protected resources, we want to see if there's a token and then
+ // add the token if it exists.
+ if (config.url.indexOf(resource) > -1) {
+ var token = AccessToken.get();
+ if (token) {
+ config.headers.Authorization = 'Bearer ' + token.access_token;
+ }
+ }
+ });
+
+ return config;
+ }
+ };
+});
+'use strict';
+
+var interceptorService = angular.module('oauth.interceptor', []);
+
+interceptorService.factory('ExpiredInterceptor', ['Storage', '$rootScope', function (Storage, $rootScope) {
+
+ var service = {};
+
+ service.request = function(config) {
+ var token = Storage.get('token');
+
+ if (token && expired(token)) {
+ $rootScope.$broadcast('oauth:expired', token);
+ }
+
+ return config;
+ };
+
+ var expired = function(token) {
+ return (token && token.expires_at && new Date(token.expires_at) < new Date());
+ };
+
+ return service;
+}]);
+
+'use strict';
+
+var directives = angular.module('oauth.directive', []);
+
+directives.directive('oauth', [
+ 'AccessToken',
+ 'Endpoint',
+ 'Profile',
+ 'Storage',
+ '$location',
+ '$rootScope',
+ '$compile',
+ '$http',
+ '$templateCache',
+ function(AccessToken, Endpoint, Profile, Storage, $location, $rootScope, $compile, $http, $templateCache) {
+
+ var definition = {
+ restrict: 'AE',
+ replace: true,
+ scope: {
+ site: '@', // (required) set the oauth server host (e.g. http://oauth.example.com)
+ clientId: '@', // (required) client id
+ redirectUri: '@', // (required) client redirect uri
+ responseType: '@', // (optional) response type, defaults to token (use 'token' for implicit flow and 'code' for authorization code flow
+ scope: '@', // (optional) scope
+ profileUri: '@', // (optional) user profile uri (e.g http://example.com/me)
+ template: '@', // (optional) template to render (e.g bower_components/oauth-ng/dist/views/templates/default.html)
+ text: '@', // (optional) login text
+ authorizePath: '@', // (optional) authorization url
+ state: '@', // (optional) An arbitrary unique string created by your app to guard against Cross-site Request Forgery
+ storage: '@' // (optional) Store token in 'sessionStorage' or 'localStorage', defaults to 'sessionStorage'
+ }
+ };
+
+ definition.link = function postLink(scope, element) {
+ scope.show = 'none';
+
+ scope.$watch('clientId', function() {
+ init();
+ });
+
+ var init = function() {
+ initAttributes(); // sets defaults
+ Storage.use(scope.storage);// set storage
+ compile(); // compiles the desired layout
+ Endpoint.set(scope); // sets the oauth authorization url
+ AccessToken.set(scope); // sets the access token object (if existing, from fragment or session)
+ initProfile(scope); // gets the profile resource (if existing the access token)
+ initView(); // sets the view (logged in or out)
+ };
+
+ var initAttributes = function() {
+ scope.authorizePath = scope.authorizePath || '/oauth/authorize';
+ scope.tokenPath = scope.tokenPath || '/oauth/token';
+ scope.template = scope.template || 'bower_components/oauth-ng/dist/views/templates/default.html';
+ scope.responseType = scope.responseType || 'token';
+ scope.text = scope.text || 'Sign In';
+ scope.state = scope.state || undefined;
+ scope.scope = scope.scope || undefined;
+ scope.storage = scope.storage || 'sessionStorage';
+ };
+
+ var compile = function() {
+ $http.get(scope.template, { cache: $templateCache }).success(function(html) {
+ element.html(html);
+ $compile(element.contents())(scope);
+ });
+ };
+
+ var initProfile = function(scope) {
+ var token = AccessToken.get();
+
+ if (token && token.access_token && scope.profileUri) {
+ Profile.find(scope.profileUri).success(function(response) {
+ scope.profile = response;
+ });
+ }
+ };
+
+ var initView = function() {
+ var token = AccessToken.get();
+
+ if (!token) {
+ return loggedOut(); // without access token it's logged out
+ }
+ if (token.access_token) {
+ return authorized(); // if there is the access token we are done
+ }
+ if (token.error) {
+ return denied(); // if the request has been denied we fire the denied event
+ }
+ };
+
+ scope.login = function() {
+ Endpoint.redirect();
+ };
+
+ scope.logout = function() {
+ AccessToken.destroy(scope);
+ $rootScope.$broadcast('oauth:logout');
+ loggedOut();
+ };
+
+ scope.$on('oauth:expired', function() {
+ AccessToken.destroy(scope);
+ scope.show = 'logged-out';
+ });
+
+ // user is authorized
+ var authorized = function() {
+ $rootScope.$broadcast('oauth:authorized', AccessToken.get());
+ scope.show = 'logged-in';
+ };
+
+ // set the oauth directive to the logged-out status
+ var loggedOut = function() {
+ $rootScope.$broadcast('oauth:loggedOut');
+ scope.show = 'logged-out';
+ };
+
+ // set the oauth directive to the denied status
+ var denied = function() {
+ scope.show = 'denied';
+ $rootScope.$broadcast('oauth:denied');
+ };
+
+ // Updates the template at runtime
+ scope.$on('oauth:template:update', function(event, template) {
+ scope.template = template;
+ compile(scope);
+ });
+
+ // Hack to update the directive content on logout
+ // TODO think to a cleaner solution
+ scope.$on('$routeChangeSuccess', function () {
+ init();
+ });
+ };
+
+ return definition;
+ }
+]);