├── .gitignore ├── README.md ├── build.gradle ├── gradle.properties ├── gradle └── wrapper │ ├── gradle-wrapper.jar │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat ├── package.json ├── settings.gradle └── src ├── main ├── java │ ├── com │ │ └── auth0 │ │ │ └── spring │ │ │ └── security │ │ │ └── auth0 │ │ │ ├── Auth0AuthenticationEntryPoint.java │ │ │ ├── Auth0AuthenticationFilter.java │ │ │ ├── Auth0AuthenticationProvider.java │ │ │ ├── Auth0BearerAuthentication.java │ │ │ ├── Auth0JWTAuthentication.java │ │ │ └── Auth0TokenException.java │ └── griffio │ │ └── auth0 │ │ └── spring │ │ ├── Main.java │ │ ├── mvc │ │ ├── HandshakeController.java │ │ ├── SubjectToken.java │ │ └── WebConfig.java │ │ └── security │ │ ├── DefaultSecurityConfigurer.java │ │ └── SecuredSecurityConfigurer.java └── resources │ ├── banner.txt │ ├── config │ └── application.properties │ └── static │ ├── favicon.ico │ ├── index.html │ └── index.js └── test ├── java └── griffio │ └── auth0 │ ├── ParseBearerTokenTest.java │ ├── jwt │ └── JWTParserTest.java │ └── spring │ └── WebContextTests.java └── resources └── config └── application.properties /.gitignore: -------------------------------------------------------------------------------- 1 | # Created by http://www.gitignore.io 2 | 3 | ### Java ### 4 | *.class 5 | 6 | # Mobile Tools for Java (J2ME) 7 | .mtj.tmp/ 8 | 9 | # Package Files # 10 | #*.jar 11 | *.war 12 | *.ear 13 | 14 | # virtual machine crash logs, see http://www.java.com/en/download/help/error_hotspot.xml 15 | hs_err_pid* 16 | 17 | # Created by http://www.gitignore.io 18 | 19 | ### Intellij ### 20 | # Covers JetBrains IDEs: IntelliJ, RubyMine, PhpStorm, AppCode, PyCharm 21 | 22 | ## Directory-based project format 23 | .idea/ 24 | # if you remove the above rule, at least ignore user-specific stuff: 25 | # .idea/workspace.xml 26 | # .idea/tasks.xml 27 | # and these sensitive or high-churn files: 28 | # .idea/dataSources.ids 29 | # .idea/dataSources.xml 30 | # .idea/sqlDataSources.xml 31 | # .idea/dynamic.xml 32 | 33 | ## File-based project format 34 | *.ipr 35 | *.iws 36 | *.iml 37 | 38 | ## Additional for IntelliJ 39 | out/ 40 | classes/ 41 | 42 | # generated by mpeltonen/sbt-idea plugin 43 | .idea_modules/ 44 | 45 | # generated by JIRA plugin 46 | atlassian-ide-plugin.xml 47 | 48 | # generated by Crashlytics plugin (for Android Studio and Intellij) 49 | com_crashlytics_export_strings.xml 50 | 51 | # Created by http://www.gitignore.io 52 | 53 | ### Gradle ### 54 | .gradle 55 | build/ 56 | 57 | ### OS X ### 58 | .DS_Store 59 | 60 | node_modules/ 61 | npm-debug.log 62 | src/main/resources/static/bundle.js 63 | static/bundle.js -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Auth0 + Spring API Seed 2 | 3 | https://auth0.com/ 4 | 5 | ## Spring Boot, Spring Framework (4.3.2), Spring Security Java Config 6 | 7 | Gradle project with Spring Boot 1.4.0, Spring Security Java Config and JWT (io.jsonwebtoken:jjwt) library and Auth0 Lock 9.2.x. 8 | 9 | Tested on Node v6.2.1, npm v3.9.5 10 | This example uses a simple JWT as the bearer token. 11 | 12 | The JWT is produced by a successful authentication with Auth0, so will require a user account setup. 13 | 14 | Example encoded JSON web token format (see http://jwt.io/) to debug. 15 | ~~~ 16 | eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL2dyaWZmaW8tYXBwbGljYXRpb24uYXV0aDAuY29tLyIsInN1YiI6ImF1dGgwfDU0YzkzMmJhYTUxNzAzYWUwNGMxMjQyMCIsImF1ZCI6IkZaN0FjdXNqZDFCRWpmNG5iZGlkNng5UFRKTEJyRThQIiwiZXhwIjoxNDI2OTgyNjQ5LCJpYXQiOjE0MjY5NDY2NDl9.o1eJwoC69jC9_hNePGts9vHUR79YiSS_hZybFQ1weeU 17 | ~~~ 18 | 19 | ~~~json 20 | { 21 | "iss": "https://example.auth0.com/", 22 | "sub": "auth0|11c111baa1111ae01c11111", 23 | "aud": "TheClientId", 24 | "exp": 1426964798, 25 | "iat": 1426928798 26 | } 27 | ~~~ 28 | 29 | The Resource Server (via Spring Security) acts as a simple turn-style granting access when a valid token is presented in the Authorisation header. 30 | 31 | #Running the example 32 | 33 | You need to create an Auth0 application with a valid database user account to login via the application page http://localhost:8080/index.html. 34 | 35 | Change the setupPage function values in “src/main/resources/static/index.js” to your application domain and client id provided by Auth0 (apps/APIs) in the settings page of your application. 36 | 37 | The JavaScript client requests the UserInfo resource from auth0 and calls the application resource with the bearer token. 38 | 39 | ~~~javascript 40 | function setupPage() { 41 | var domain = 'FIXME'; // myapp-application.auth0.com 42 | var cid = 'FIXME'; // ZZZYzzzz1ZZyy2yyyyy1x0YYYYxYYY 43 | ... 44 | } 45 | ~~~ 46 | 47 | ~~~ 48 | npm install 49 | ~~~ 50 | 51 | Browserify the client 52 | 53 | ~~~ 54 | npm run bundle 55 | ~~~ 56 | 57 | The ClientSecret, ClientId and Domain for Auth0 app are not supplied and are provided from Auth0 (Apps/APIs). 58 | 59 | The ClientSecret is a url-base64 encoded from the Auth0 application config. Use the copy button in Auth0. 60 | 61 | The Domain url entered here must be similar to this example:- "https://myapp-application.auth0.com/“. 62 | 63 | --- 64 | 65 | ### Spring Boot plugin runner 66 | 67 | You can set these variables in the `application.properties` file, or you can set them as arguments when running the app. 68 | 69 | ~~~ 70 | ./gradlew bootRun 71 | ~~~ 72 | 73 | Gradle processes use -P properties that can then pass arguments in the build script to the JavaExec process. 74 | 75 | ~~~ 76 | ./gradlew bootRun -Pauth0="--auth0.clientId=FIXME,--auth0.clientSecret=FIXME,--auth0.domain=FIXME" 77 | ~~~ 78 | 79 | Browser page is for login is:- http://localhost:8080 and displays auth0 login page. 80 | 81 | Test without auth on command line 82 | 83 | ~~~ 84 | curl -v http://localhost:8080/handshake 85 | ~~~ 86 | 87 | Auth token required for authorised handshake 88 | 89 | ~~~ 90 | curl -v --header "Authorization: Bearer " http://localhost:8080/authorised/handshake 91 | ~~~ 92 | -------------------------------------------------------------------------------- /build.gradle: -------------------------------------------------------------------------------- 1 | buildscript { 2 | repositories { 3 | mavenCentral() 4 | } 5 | dependencies { 6 | classpath("org.springframework.boot:spring-boot-gradle-plugin:${spring_boot_gradle_plugin_version}") 7 | } 8 | } 9 | 10 | plugins { 11 | id 'java' 12 | } 13 | 14 | apply plugin: 'spring-boot' 15 | 16 | group = 'griffio' 17 | version = '1.0.0-SNAPSHOT' 18 | description = "spring-security-auth0-api-example" 19 | 20 | repositories { 21 | mavenCentral() 22 | } 23 | 24 | dependencies { 25 | compile( 26 | 'org.springframework.boot:spring-boot-starter-web', 27 | 'org.springframework.boot:spring-boot-starter-security', 28 | 'io.jsonwebtoken:jjwt:0.6.0', 29 | 'com.google.guava:guava:18.0') 30 | 31 | testCompile( 32 | 'junit:junit', 33 | 'org.springframework.boot:spring-boot-starter-test') 34 | } 35 | 36 | tasks.withType(JavaCompile) { 37 | description = "javac: ignore processor hints, keep all other hints" 38 | sourceCompatibility = JavaVersion.VERSION_1_8 39 | targetCompatibility = JavaVersion.VERSION_1_8 40 | options.compilerArgs += ["-Xlint:all,-processing"] 41 | } 42 | 43 | tasks.withType(JavaExec) { 44 | if (project.hasProperty('auth0')) { 45 | args(project.auth0.split(",")) 46 | } 47 | } 48 | 49 | task wrapper(type: Wrapper) { 50 | gradleVersion = '2.14.1' 51 | } 52 | 53 | 54 | -------------------------------------------------------------------------------- /gradle.properties: -------------------------------------------------------------------------------- 1 | spring_boot_gradle_plugin_version=1.4.0.RELEASE -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/griffio/spring-security-auth0-sample/e6780a770a95834c4695a37b5127e6931ff3c128/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | #Fri Jul 29 22:29:59 BST 2016 2 | distributionBase=GRADLE_USER_HOME 3 | distributionPath=wrapper/dists 4 | zipStoreBase=GRADLE_USER_HOME 5 | zipStorePath=wrapper/dists 6 | distributionUrl=https\://services.gradle.org/distributions/gradle-2.14.1-bin.zip 7 | -------------------------------------------------------------------------------- /gradlew: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | ############################################################################## 4 | ## 5 | ## Gradle start up script for UN*X 6 | ## 7 | ############################################################################## 8 | 9 | # Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 10 | DEFAULT_JVM_OPTS="" 11 | 12 | APP_NAME="Gradle" 13 | APP_BASE_NAME=`basename "$0"` 14 | 15 | # Use the maximum available, or set MAX_FD != -1 to use that value. 16 | MAX_FD="maximum" 17 | 18 | warn ( ) { 19 | echo "$*" 20 | } 21 | 22 | die ( ) { 23 | echo 24 | echo "$*" 25 | echo 26 | exit 1 27 | } 28 | 29 | # OS specific support (must be 'true' or 'false'). 30 | cygwin=false 31 | msys=false 32 | darwin=false 33 | case "`uname`" in 34 | CYGWIN* ) 35 | cygwin=true 36 | ;; 37 | Darwin* ) 38 | darwin=true 39 | ;; 40 | MINGW* ) 41 | msys=true 42 | ;; 43 | esac 44 | 45 | # For Cygwin, ensure paths are in UNIX format before anything is touched. 46 | if $cygwin ; then 47 | [ -n "$JAVA_HOME" ] && JAVA_HOME=`cygpath --unix "$JAVA_HOME"` 48 | fi 49 | 50 | # Attempt to set APP_HOME 51 | # Resolve links: $0 may be a link 52 | PRG="$0" 53 | # Need this for relative symlinks. 54 | while [ -h "$PRG" ] ; do 55 | ls=`ls -ld "$PRG"` 56 | link=`expr "$ls" : '.*-> \(.*\)$'` 57 | if expr "$link" : '/.*' > /dev/null; then 58 | PRG="$link" 59 | else 60 | PRG=`dirname "$PRG"`"/$link" 61 | fi 62 | done 63 | SAVED="`pwd`" 64 | cd "`dirname \"$PRG\"`/" >&- 65 | APP_HOME="`pwd -P`" 66 | cd "$SAVED" >&- 67 | 68 | CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar 69 | 70 | # Determine the Java command to use to start the JVM. 71 | if [ -n "$JAVA_HOME" ] ; then 72 | if [ -x "$JAVA_HOME/jre/sh/java" ] ; then 73 | # IBM's JDK on AIX uses strange locations for the executables 74 | JAVACMD="$JAVA_HOME/jre/sh/java" 75 | else 76 | JAVACMD="$JAVA_HOME/bin/java" 77 | fi 78 | if [ ! -x "$JAVACMD" ] ; then 79 | die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME 80 | 81 | Please set the JAVA_HOME variable in your environment to match the 82 | location of your Java installation." 83 | fi 84 | else 85 | JAVACMD="java" 86 | which java >/dev/null 2>&1 || die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 87 | 88 | Please set the JAVA_HOME variable in your environment to match the 89 | location of your Java installation." 90 | fi 91 | 92 | # Increase the maximum file descriptors if we can. 93 | if [ "$cygwin" = "false" -a "$darwin" = "false" ] ; then 94 | MAX_FD_LIMIT=`ulimit -H -n` 95 | if [ $? -eq 0 ] ; then 96 | if [ "$MAX_FD" = "maximum" -o "$MAX_FD" = "max" ] ; then 97 | MAX_FD="$MAX_FD_LIMIT" 98 | fi 99 | ulimit -n $MAX_FD 100 | if [ $? -ne 0 ] ; then 101 | warn "Could not set maximum file descriptor limit: $MAX_FD" 102 | fi 103 | else 104 | warn "Could not query maximum file descriptor limit: $MAX_FD_LIMIT" 105 | fi 106 | fi 107 | 108 | # For Darwin, add options to specify how the application appears in the dock 109 | if $darwin; then 110 | GRADLE_OPTS="$GRADLE_OPTS \"-Xdock:name=$APP_NAME\" \"-Xdock:icon=$APP_HOME/media/gradle.icns\"" 111 | fi 112 | 113 | # For Cygwin, switch paths to Windows format before running java 114 | if $cygwin ; then 115 | APP_HOME=`cygpath --path --mixed "$APP_HOME"` 116 | CLASSPATH=`cygpath --path --mixed "$CLASSPATH"` 117 | 118 | # We build the pattern for arguments to be converted via cygpath 119 | ROOTDIRSRAW=`find -L / -maxdepth 1 -mindepth 1 -type d 2>/dev/null` 120 | SEP="" 121 | for dir in $ROOTDIRSRAW ; do 122 | ROOTDIRS="$ROOTDIRS$SEP$dir" 123 | SEP="|" 124 | done 125 | OURCYGPATTERN="(^($ROOTDIRS))" 126 | # Add a user-defined pattern to the cygpath arguments 127 | if [ "$GRADLE_CYGPATTERN" != "" ] ; then 128 | OURCYGPATTERN="$OURCYGPATTERN|($GRADLE_CYGPATTERN)" 129 | fi 130 | # Now convert the arguments - kludge to limit ourselves to /bin/sh 131 | i=0 132 | for arg in "$@" ; do 133 | CHECK=`echo "$arg"|egrep -c "$OURCYGPATTERN" -` 134 | CHECK2=`echo "$arg"|egrep -c "^-"` ### Determine if an option 135 | 136 | if [ $CHECK -ne 0 ] && [ $CHECK2 -eq 0 ] ; then ### Added a condition 137 | eval `echo args$i`=`cygpath --path --ignore --mixed "$arg"` 138 | else 139 | eval `echo args$i`="\"$arg\"" 140 | fi 141 | i=$((i+1)) 142 | done 143 | case $i in 144 | (0) set -- ;; 145 | (1) set -- "$args0" ;; 146 | (2) set -- "$args0" "$args1" ;; 147 | (3) set -- "$args0" "$args1" "$args2" ;; 148 | (4) set -- "$args0" "$args1" "$args2" "$args3" ;; 149 | (5) set -- "$args0" "$args1" "$args2" "$args3" "$args4" ;; 150 | (6) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" ;; 151 | (7) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" ;; 152 | (8) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" ;; 153 | (9) set -- "$args0" "$args1" "$args2" "$args3" "$args4" "$args5" "$args6" "$args7" "$args8" ;; 154 | esac 155 | fi 156 | 157 | # Split up the JVM_OPTS And GRADLE_OPTS values into an array, following the shell quoting and substitution rules 158 | function splitJvmOpts() { 159 | JVM_OPTS=("$@") 160 | } 161 | eval splitJvmOpts $DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS 162 | JVM_OPTS[${#JVM_OPTS[*]}]="-Dorg.gradle.appname=$APP_BASE_NAME" 163 | 164 | exec "$JAVACMD" "${JVM_OPTS[@]}" -classpath "$CLASSPATH" org.gradle.wrapper.GradleWrapperMain "$@" 165 | -------------------------------------------------------------------------------- /gradlew.bat: -------------------------------------------------------------------------------- 1 | @if "%DEBUG%" == "" @echo off 2 | @rem ########################################################################## 3 | @rem 4 | @rem Gradle startup script for Windows 5 | @rem 6 | @rem ########################################################################## 7 | 8 | @rem Set local scope for the variables with windows NT shell 9 | if "%OS%"=="Windows_NT" setlocal 10 | 11 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 12 | set DEFAULT_JVM_OPTS= 13 | 14 | set DIRNAME=%~dp0 15 | if "%DIRNAME%" == "" set DIRNAME=. 16 | set APP_BASE_NAME=%~n0 17 | set APP_HOME=%DIRNAME% 18 | 19 | @rem Find java.exe 20 | if defined JAVA_HOME goto findJavaFromJavaHome 21 | 22 | set JAVA_EXE=java.exe 23 | %JAVA_EXE% -version >NUL 2>&1 24 | if "%ERRORLEVEL%" == "0" goto init 25 | 26 | echo. 27 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 28 | echo. 29 | echo Please set the JAVA_HOME variable in your environment to match the 30 | echo location of your Java installation. 31 | 32 | goto fail 33 | 34 | :findJavaFromJavaHome 35 | set JAVA_HOME=%JAVA_HOME:"=% 36 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 37 | 38 | if exist "%JAVA_EXE%" goto init 39 | 40 | echo. 41 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 42 | echo. 43 | echo Please set the JAVA_HOME variable in your environment to match the 44 | echo location of your Java installation. 45 | 46 | goto fail 47 | 48 | :init 49 | @rem Get command-line arguments, handling Windowz variants 50 | 51 | if not "%OS%" == "Windows_NT" goto win9xME_args 52 | if "%@eval[2+2]" == "4" goto 4NT_args 53 | 54 | :win9xME_args 55 | @rem Slurp the command line arguments. 56 | set CMD_LINE_ARGS= 57 | set _SKIP=2 58 | 59 | :win9xME_args_slurp 60 | if "x%~1" == "x" goto execute 61 | 62 | set CMD_LINE_ARGS=%* 63 | goto execute 64 | 65 | :4NT_args 66 | @rem Get arguments from the 4NT Shell from JP Software 67 | set CMD_LINE_ARGS=%$ 68 | 69 | :execute 70 | @rem Setup the command line 71 | 72 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 73 | 74 | @rem Execute Gradle 75 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %CMD_LINE_ARGS% 76 | 77 | :end 78 | @rem End local scope for the variables with windows NT shell 79 | if "%ERRORLEVEL%"=="0" goto mainEnd 80 | 81 | :fail 82 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 83 | rem the _cmd.exe /c_ return code! 84 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 85 | exit /b 1 86 | 87 | :mainEnd 88 | if "%OS%"=="Windows_NT" endlocal 89 | 90 | :omega 91 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "spring-security-auth0-sample", 3 | "version": "1.0.0", 4 | "description": "auth0 spring security", 5 | "main": "index.js", 6 | "scripts": { 7 | "test": "echo \"Error: no test specified\" && exit 1", 8 | "bundle": "browserify --entry src/main/resources/static/index.js --outfile src/main/resources/static/bundle.js --debug", 9 | "serve": "beefy src/main/resources/static/index.js:bundle.js --open" 10 | }, 11 | "browserify": { 12 | "transform": [ 13 | "brfs", 14 | "ejsify", 15 | "packageify" 16 | ] 17 | }, 18 | "repository": { 19 | "type": "git", 20 | "url": "git+https://github.com/griffio/spring-security-auth0-sample.git" 21 | }, 22 | "keywords": [ 23 | "auth0" 24 | ], 25 | "author": "griffio", 26 | "license": "ISC", 27 | "bugs": { 28 | "url": "https://github.com/griffio/spring-security-auth0-sample/issues" 29 | }, 30 | "homepage": "https://github.com/griffio/spring-security-auth0-sample", 31 | "dependencies": { 32 | "auth0-lock": "^9.2.1", 33 | "browserify": "^13.0.1", 34 | "jsonwebtoken": "^7.0.0", 35 | "reqwest": "^2.0.5" 36 | }, 37 | "devDependencies": { 38 | "brfs": "^1.3.0", 39 | "ejsify": "^1.0.0", 40 | "packageify": "^0.2.2" 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /settings.gradle: -------------------------------------------------------------------------------- 1 | rootProject.name = 'spring-security-auth0-api-example' 2 | -------------------------------------------------------------------------------- /src/main/java/com/auth0/spring/security/auth0/Auth0AuthenticationEntryPoint.java: -------------------------------------------------------------------------------- 1 | package com.auth0.spring.security.auth0; 2 | 3 | import java.io.IOException; 4 | import java.io.PrintWriter; 5 | 6 | import javax.servlet.ServletException; 7 | import javax.servlet.http.HttpServletRequest; 8 | import javax.servlet.http.HttpServletResponse; 9 | 10 | import org.springframework.security.core.AuthenticationException; 11 | import org.springframework.security.web.AuthenticationEntryPoint; 12 | 13 | public class Auth0AuthenticationEntryPoint implements AuthenticationEntryPoint { 14 | 15 | @Override 16 | public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException authException) 17 | throws IOException, ServletException { 18 | 19 | PrintWriter writer = response.getWriter(); 20 | 21 | if (isPreflight(request)) { 22 | response.setStatus(HttpServletResponse.SC_NO_CONTENT); 23 | } else if (authException instanceof Auth0TokenException) { 24 | response.sendError(HttpServletResponse.SC_UNAUTHORIZED, authException.getMessage()); 25 | writer.println("HTTP Status " + HttpServletResponse.SC_UNAUTHORIZED + " - " + authException.getMessage()); 26 | } else { 27 | response.sendError(HttpServletResponse.SC_FORBIDDEN, authException.getMessage()); 28 | writer.println("HTTP Status " + HttpServletResponse.SC_FORBIDDEN + " - " + authException.getMessage()); 29 | } 30 | } 31 | 32 | private boolean isPreflight(HttpServletRequest request) { 33 | return "OPTIONS".equals(request.getMethod()); 34 | } 35 | 36 | } -------------------------------------------------------------------------------- /src/main/java/com/auth0/spring/security/auth0/Auth0AuthenticationFilter.java: -------------------------------------------------------------------------------- 1 | package com.auth0.spring.security.auth0; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.security.authentication.AuthenticationManager; 6 | import org.springframework.security.core.Authentication; 7 | import org.springframework.security.core.AuthenticationException; 8 | import org.springframework.security.core.context.SecurityContextHolder; 9 | import org.springframework.security.web.authentication.AuthenticationFailureHandler; 10 | import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler; 11 | import org.springframework.util.StringUtils; 12 | import org.springframework.web.filter.GenericFilterBean; 13 | 14 | import javax.servlet.FilterChain; 15 | import javax.servlet.ServletException; 16 | import javax.servlet.ServletRequest; 17 | import javax.servlet.ServletResponse; 18 | import javax.servlet.http.HttpServletRequest; 19 | import javax.servlet.http.HttpServletResponse; 20 | import java.io.IOException; 21 | import java.text.ParseException; 22 | 23 | public class Auth0AuthenticationFilter extends GenericFilterBean { 24 | 25 | private static final Logger logger = LoggerFactory.getLogger(Auth0AuthenticationFilter.class); 26 | 27 | private final AuthenticationFailureHandler failureHandler; 28 | 29 | private AuthenticationManager authenticationManager; 30 | 31 | public Auth0AuthenticationFilter() { 32 | this.failureHandler = new SimpleUrlAuthenticationFailureHandler(); 33 | } 34 | 35 | @Override 36 | public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { 37 | 38 | HttpServletRequest httpRequest = (HttpServletRequest) request; 39 | HttpServletResponse httpResponse = (HttpServletResponse) response; 40 | 41 | String authorizationHeader = httpRequest.getHeader("Authorization"); 42 | 43 | try { 44 | if (StringUtils.isEmpty(authorizationHeader)) { 45 | throw new Auth0TokenException("Authorization header missing from request. Expected Authorization:"); 46 | } 47 | try { 48 | logger.info(authorizationHeader); 49 | Auth0BearerAuthentication authentication = Auth0BearerAuthentication.create(authorizationHeader); 50 | Authentication authenticated = authenticationManager().authenticate(authentication); 51 | SecurityContextHolder.getContext().setAuthentication(authenticated); 52 | chain.doFilter(request, response); 53 | } catch (ParseException e) { 54 | throw new Auth0TokenException(e); 55 | } 56 | } catch (AuthenticationException e) { 57 | logger.error("AuthenticationException", e); 58 | failureHandler.onAuthenticationFailure(httpRequest, httpResponse, e); 59 | } 60 | 61 | } 62 | 63 | public AuthenticationManager authenticationManager() { 64 | return authenticationManager; 65 | } 66 | 67 | public void setAuthenticationManager(AuthenticationManager authenticationManager) { 68 | this.authenticationManager = authenticationManager; 69 | } 70 | } -------------------------------------------------------------------------------- /src/main/java/com/auth0/spring/security/auth0/Auth0AuthenticationProvider.java: -------------------------------------------------------------------------------- 1 | package com.auth0.spring.security.auth0; 2 | 3 | import io.jsonwebtoken.Claims; 4 | import io.jsonwebtoken.Jws; 5 | import io.jsonwebtoken.Jwts; 6 | import org.springframework.beans.factory.InitializingBean; 7 | import org.springframework.security.authentication.AuthenticationProvider; 8 | import org.springframework.security.core.Authentication; 9 | import org.springframework.security.core.AuthenticationException; 10 | 11 | import java.util.Base64; 12 | 13 | public class Auth0AuthenticationProvider implements AuthenticationProvider, InitializingBean { 14 | 15 | private final String clientId; 16 | private final String clientSecret; 17 | private final String issuer; 18 | 19 | public Auth0AuthenticationProvider(String clientId, String clientSecret, String issuer) { 20 | this.clientId = clientId; 21 | this.clientSecret = clientSecret; 22 | this.issuer = issuer; 23 | } 24 | 25 | public Authentication authenticate(Authentication authentication) throws AuthenticationException { 26 | 27 | if (!(authentication instanceof Auth0BearerAuthentication)) { 28 | return null; 29 | } 30 | 31 | try { 32 | String bearerToken = authentication.getPrincipal().toString(); 33 | byte[] signingKey = Base64.getUrlDecoder().decode(clientSecret); 34 | Jws jws = Jwts.parser().setSigningKey(signingKey).parseClaimsJws(bearerToken); 35 | Claims claims = jws.getBody(); 36 | return Auth0JWTAuthentication.create(claims); 37 | } catch (Exception e) { 38 | throw new Auth0TokenException(e); 39 | } 40 | 41 | } 42 | 43 | public boolean supports(Class authentication) { 44 | return Auth0BearerAuthentication.class.isAssignableFrom(authentication); 45 | } 46 | 47 | @Override 48 | public void afterPropertiesSet() throws Exception { 49 | } 50 | 51 | public String getClientId() { 52 | return clientId; 53 | } 54 | 55 | public String getIssuer() { 56 | return issuer; 57 | } 58 | 59 | public String getClientSecret() { 60 | return clientSecret; 61 | } 62 | } -------------------------------------------------------------------------------- /src/main/java/com/auth0/spring/security/auth0/Auth0BearerAuthentication.java: -------------------------------------------------------------------------------- 1 | package com.auth0.spring.security.auth0; 2 | 3 | import org.springframework.security.web.authentication.preauth.PreAuthenticatedAuthenticationToken; 4 | import org.springframework.util.StringUtils; 5 | 6 | import java.text.ParseException; 7 | 8 | public class Auth0BearerAuthentication extends PreAuthenticatedAuthenticationToken { 9 | 10 | private static final long serialVersionUID = 1L; 11 | 12 | public Auth0BearerAuthentication(String principal) { 13 | super(principal, ""); 14 | } 15 | 16 | public static Auth0BearerAuthentication create(String bearer) throws ParseException { 17 | 18 | if (StringUtils.isEmpty(bearer)) { 19 | throw new ParseException("Bearer token expected", 0); 20 | } 21 | 22 | String[] parts = bearer.split("\\s", 2); 23 | 24 | if (!parts[0].equals("Bearer")) { 25 | throw new ParseException("Token type must be Bearer", 0); 26 | } 27 | 28 | if (parts.length != 2) { 29 | throw new ParseException("Bearer requires value", "Bearer".length()); 30 | } 31 | 32 | return new Auth0BearerAuthentication(parts[1].trim()); 33 | 34 | } 35 | 36 | } 37 | -------------------------------------------------------------------------------- /src/main/java/com/auth0/spring/security/auth0/Auth0JWTAuthentication.java: -------------------------------------------------------------------------------- 1 | package com.auth0.spring.security.auth0; 2 | 3 | import io.jsonwebtoken.Claims; 4 | import org.springframework.security.authentication.AbstractAuthenticationToken; 5 | import org.springframework.security.core.authority.AuthorityUtils; 6 | /** 7 | The claims provide the following attributes (example values). 8 | { 9 | "iss": "https://example.auth0.com/", 10 | "sub": "auth0|11c111baa1111ae01c11111", 11 | "aud": "TheClientId", 12 | "exp": 1426964798, 13 | "iat": 1426928798 14 | } 15 | */ 16 | public class Auth0JWTAuthentication extends AbstractAuthenticationToken { 17 | 18 | private static final long serialVersionUID = 1L; 19 | 20 | private Object principal; 21 | private Object credentials; 22 | 23 | @Override 24 | public Object getCredentials() { 25 | return credentials; 26 | } 27 | 28 | public Auth0JWTAuthentication(Claims claims) { 29 | super(AuthorityUtils.commaSeparatedStringToAuthorityList("ROLE_USER")); 30 | this.principal = String.format("subject:%s@%s", claims.getSubject(), claims.getIssuer()); 31 | this.credentials = claims; 32 | setAuthenticated(true); 33 | } 34 | 35 | @Override 36 | public Object getPrincipal() { 37 | return this.principal; 38 | } 39 | 40 | public static Auth0JWTAuthentication create(Claims claims) { 41 | return new Auth0JWTAuthentication(claims); 42 | } 43 | 44 | } -------------------------------------------------------------------------------- /src/main/java/com/auth0/spring/security/auth0/Auth0TokenException.java: -------------------------------------------------------------------------------- 1 | package com.auth0.spring.security.auth0; 2 | 3 | import org.springframework.security.core.AuthenticationException; 4 | 5 | public class Auth0TokenException extends AuthenticationException { 6 | 7 | private static final long serialVersionUID = 1L; 8 | 9 | public Auth0TokenException(String msg) { 10 | super(msg); 11 | } 12 | 13 | public Auth0TokenException(String msg, Throwable t) { 14 | super(msg, t); 15 | } 16 | 17 | public Auth0TokenException(Exception e) { 18 | super(e.getMessage(), e); 19 | } 20 | 21 | } 22 | -------------------------------------------------------------------------------- /src/main/java/griffio/auth0/spring/Main.java: -------------------------------------------------------------------------------- 1 | package griffio.auth0.spring; 2 | 3 | import org.springframework.boot.SpringApplication; 4 | import org.springframework.boot.autoconfigure.SpringBootApplication; 5 | 6 | @SpringBootApplication 7 | public class Main { 8 | public static void main(String[] args) { 9 | SpringApplication.run(Main.class, args); 10 | } 11 | 12 | } 13 | -------------------------------------------------------------------------------- /src/main/java/griffio/auth0/spring/mvc/HandshakeController.java: -------------------------------------------------------------------------------- 1 | package griffio.auth0.spring.mvc; 2 | 3 | import org.slf4j.Logger; 4 | import org.slf4j.LoggerFactory; 5 | import org.springframework.stereotype.Controller; 6 | import org.springframework.web.bind.annotation.CrossOrigin; 7 | import org.springframework.web.bind.annotation.RequestMapping; 8 | import org.springframework.web.bind.annotation.ResponseBody; 9 | 10 | @Controller 11 | public class HandshakeController { 12 | 13 | private static Logger log = LoggerFactory.getLogger(HandshakeController.class); 14 | 15 | @RequestMapping(value = "/handshake") 16 | @ResponseBody 17 | public String handshake() { 18 | return "OK"; 19 | } 20 | 21 | @RequestMapping(value = "/authorised/handshake") 22 | @CrossOrigin 23 | @ResponseBody 24 | public String authorisedHandshake(@SubjectToken String tokenSubject) { 25 | log.info(tokenSubject); 26 | return "OK"; 27 | } 28 | 29 | } -------------------------------------------------------------------------------- /src/main/java/griffio/auth0/spring/mvc/SubjectToken.java: -------------------------------------------------------------------------------- 1 | package griffio.auth0.spring.mvc; 2 | 3 | import org.springframework.security.core.annotation.AuthenticationPrincipal; 4 | 5 | import java.lang.annotation.ElementType; 6 | import java.lang.annotation.Retention; 7 | import java.lang.annotation.RetentionPolicy; 8 | import java.lang.annotation.Target; 9 | 10 | @Target({ElementType.PARAMETER, ElementType.TYPE}) 11 | @Retention(RetentionPolicy.RUNTIME) 12 | @AuthenticationPrincipal 13 | public @interface SubjectToken { 14 | } -------------------------------------------------------------------------------- /src/main/java/griffio/auth0/spring/mvc/WebConfig.java: -------------------------------------------------------------------------------- 1 | package griffio.auth0.spring.mvc; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity; 5 | import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; 6 | import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry; 7 | import org.springframework.web.servlet.config.annotation.WebMvcConfigurerAdapter; 8 | 9 | /** 10 | * see org.springframework.boot.autoconfigure.web.WebMvcAutoConfiguration for details on automatic configuration 11 | */ 12 | @Configuration 13 | @EnableWebSecurity 14 | public class WebConfig extends WebMvcConfigurerAdapter { 15 | 16 | @Override 17 | public void addResourceHandlers(ResourceHandlerRegistry registry) { 18 | } 19 | 20 | @Override 21 | public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) { 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /src/main/java/griffio/auth0/spring/security/DefaultSecurityConfigurer.java: -------------------------------------------------------------------------------- 1 | package griffio.auth0.spring.security; 2 | 3 | import org.springframework.context.annotation.Configuration; 4 | import org.springframework.core.annotation.Order; 5 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 6 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 7 | import org.springframework.security.config.http.SessionCreationPolicy; 8 | 9 | @Configuration 10 | @Order(2) 11 | public class DefaultSecurityConfigurer extends WebSecurityConfigurerAdapter { 12 | 13 | @Override 14 | protected void configure(HttpSecurity http) throws Exception { 15 | http.csrf().disable() 16 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) 17 | .and().antMatcher("/**").authorizeRequests().anyRequest().permitAll(); 18 | } 19 | } -------------------------------------------------------------------------------- /src/main/java/griffio/auth0/spring/security/SecuredSecurityConfigurer.java: -------------------------------------------------------------------------------- 1 | package griffio.auth0.spring.security; 2 | 3 | import com.auth0.spring.security.auth0.Auth0AuthenticationEntryPoint; 4 | import com.auth0.spring.security.auth0.Auth0AuthenticationFilter; 5 | import com.auth0.spring.security.auth0.Auth0AuthenticationProvider; 6 | import org.slf4j.Logger; 7 | import org.slf4j.LoggerFactory; 8 | import org.springframework.beans.factory.annotation.Value; 9 | import org.springframework.context.annotation.Bean; 10 | import org.springframework.context.annotation.Configuration; 11 | import org.springframework.core.annotation.Order; 12 | import org.springframework.security.authentication.AuthenticationProvider; 13 | import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder; 14 | import org.springframework.security.config.annotation.web.builders.HttpSecurity; 15 | import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; 16 | import org.springframework.security.config.http.SessionCreationPolicy; 17 | import org.springframework.security.web.context.SecurityContextPersistenceFilter; 18 | import org.springframework.web.cors.CorsConfiguration; 19 | import org.springframework.web.cors.CorsConfigurationSource; 20 | import org.springframework.web.cors.UrlBasedCorsConfigurationSource; 21 | 22 | import java.util.Arrays; 23 | import java.util.List; 24 | 25 | @Configuration 26 | @Order(1) 27 | public class SecuredSecurityConfigurer extends WebSecurityConfigurerAdapter { 28 | 29 | private static Logger log = LoggerFactory.getLogger(SecuredSecurityConfigurer.class); 30 | 31 | @Value(value = "${auth0.clientId}") 32 | private String clientId; 33 | 34 | @Value(value = "${auth0.clientSecret}") 35 | private String clientSecret; 36 | 37 | @Value(value = "${auth0.domain}") 38 | private String issuer; 39 | 40 | @Value(value = "${cors.allowed-origins}") 41 | private List corsAllowedOrigins; 42 | 43 | @Bean 44 | public AuthenticationProvider authenticationProvider() throws Exception { 45 | log.info("{}:{}", clientId, clientSecret); 46 | Auth0AuthenticationProvider authenticationProvider; 47 | authenticationProvider = new Auth0AuthenticationProvider(clientId, clientSecret, issuer); 48 | return authenticationProvider; 49 | } 50 | 51 | @Override 52 | protected void configure(HttpSecurity http) throws Exception { 53 | 54 | Auth0AuthenticationEntryPoint auth0AuthenticationEntryPoint; 55 | auth0AuthenticationEntryPoint = new Auth0AuthenticationEntryPoint(); 56 | 57 | Auth0AuthenticationFilter authenticationFilter; 58 | authenticationFilter = new Auth0AuthenticationFilter(); 59 | authenticationFilter.setAuthenticationManager(authenticationManager()); 60 | http.csrf().disable() 61 | .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS) 62 | .and().cors() // http://docs.spring.io/spring-security/site/docs/4.1.x/reference/htmlsingle/#cors 63 | .and().exceptionHandling().authenticationEntryPoint(auth0AuthenticationEntryPoint) 64 | .and().antMatcher("/authorised/**").authorizeRequests().anyRequest().hasRole("USER") 65 | .and().addFilterAfter(authenticationFilter, SecurityContextPersistenceFilter.class); 66 | } 67 | 68 | @Bean 69 | CorsConfigurationSource corsConfigurationSource() { 70 | CorsConfiguration configuration = new CorsConfiguration(); 71 | configuration.setAllowedOrigins(corsAllowedOrigins); 72 | configuration.setAllowedMethods(Arrays.asList("GET","POST")); 73 | UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource(); 74 | source.registerCorsConfiguration("/authorised/**", configuration); 75 | return source; 76 | } 77 | 78 | @Override 79 | protected void configure(AuthenticationManagerBuilder auth) throws Exception { 80 | auth.authenticationProvider(authenticationProvider()); 81 | } 82 | } -------------------------------------------------------------------------------- /src/main/resources/banner.txt: -------------------------------------------------------------------------------- 1 | ____ ____ ____ ____ ____ 2 | ||A |||u |||t |||h |||0 || 3 | ||__|||__|||__|||__|||__|| 4 | |/__\|/__\|/__\|/__\|/__\| 5 | ${application.name}[${application.version}] -------------------------------------------------------------------------------- /src/main/resources/config/application.properties: -------------------------------------------------------------------------------- 1 | application.name=auth0 application 2 | application.version=1.0.0 3 | logging.file=build/griffio.log 4 | auth0.clientId=FIXME 5 | auth0.clientSecret=FIXME_FIXME_FIXME_FIXME_FIXME_FIXME_FIXME_FIXME 6 | auth0.domain=FIXME 7 | 8 | cors.allowed-origins=localhost -------------------------------------------------------------------------------- /src/main/resources/static/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/griffio/spring-security-auth0-sample/e6780a770a95834c4695a37b5127e6931ff3c128/src/main/resources/static/favicon.ico -------------------------------------------------------------------------------- /src/main/resources/static/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | griffio-application.auth0.com 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /src/main/resources/static/index.js: -------------------------------------------------------------------------------- 1 | var Auth0Lock = require("auth0-lock"); 2 | var Reqwest = require("reqwest"); 3 | var jwt = require('jsonwebtoken'); 4 | 5 | (function (document, undefined) { 6 | 7 | var widget = undefined; 8 | 9 | var authTokenStore = { 10 | setToken: function (token) { localStorage.setItem("auth0token", token);}, 11 | getToken: function () {return localStorage.getItem("auth0token");} 12 | }; 13 | 14 | function setupPage() { 15 | 16 | var domain = 'FIXME.auth0.com'; 17 | var cid = 'FIXME'; 18 | 19 | widget = Auth0Lock(cid, domain); 20 | 21 | widget.show({ 22 | focusInput: false, 23 | popup: true 24 | }, function (err, profile, token) { 25 | if (err) { 26 | console.error(err); 27 | } 28 | var btn = document.createElement("button"); 29 | btn.setAttribute("id", "js-nickname"); 30 | btn.textContent = profile['nickname']; 31 | btn.onclick = makeHandShake; 32 | document.body.appendChild(btn); 33 | userProfile = profile; 34 | console.log(token); 35 | authTokenStore.setToken(token) 36 | }); 37 | 38 | } 39 | 40 | function makeHandShake(event) { 41 | var auth0token = authTokenStore.getToken(); 42 | console.log(auth0token); 43 | widget.getProfile(auth0token, function(token, userinfo) { 44 | var textarea = document.createElement("textarea"); 45 | textarea.setAttribute("cols", "15"); 46 | textarea.setAttribute("rows", "15"); 47 | textarea.textContent = JSON.stringify(userinfo, 0, 2); 48 | document.body.appendChild(textarea); 49 | }); 50 | 51 | 52 | Reqwest({ 53 | url: '/authorised/handshake' 54 | , type: 'html' 55 | , method: 'get' 56 | , contentType: 'text/plain' 57 | , headers: { 58 | 'Authorization': 'Bearer ' + auth0token 59 | } 60 | , error: function (err) { 61 | } 62 | , success: function (resp) { 63 | console.log(resp.content); 64 | } 65 | }) 66 | 67 | } 68 | 69 | setupPage(); 70 | 71 | }) 72 | (window.document, void 0); -------------------------------------------------------------------------------- /src/test/java/griffio/auth0/ParseBearerTokenTest.java: -------------------------------------------------------------------------------- 1 | package griffio.auth0; 2 | 3 | import com.auth0.spring.security.auth0.Auth0BearerAuthentication; 4 | import org.junit.Assert; 5 | import org.junit.Test; 6 | 7 | public class ParseBearerTokenTest { 8 | 9 | @Test 10 | public void parse_success() throws Exception { 11 | 12 | final String bearerToken = "Bearer 123456 "; 13 | 14 | Auth0BearerAuthentication bearerValue = Auth0BearerAuthentication.create(bearerToken); 15 | 16 | Assert.assertEquals("123456", bearerValue.getName()); 17 | 18 | } 19 | 20 | } 21 | -------------------------------------------------------------------------------- /src/test/java/griffio/auth0/jwt/JWTParserTest.java: -------------------------------------------------------------------------------- 1 | package griffio.auth0.jwt; 2 | 3 | import io.jsonwebtoken.Claims; 4 | import io.jsonwebtoken.Jws; 5 | import io.jsonwebtoken.Jwts; 6 | import io.jsonwebtoken.SignatureAlgorithm; 7 | import io.jsonwebtoken.SignatureException; 8 | import io.jsonwebtoken.impl.crypto.MacProvider; 9 | import org.junit.Assert; 10 | import org.junit.Before; 11 | import org.junit.Rule; 12 | import org.junit.Test; 13 | import org.junit.rules.ExpectedException; 14 | 15 | import java.security.Key; 16 | 17 | public class JWTParserTest { 18 | 19 | @Rule 20 | public ExpectedException thrown = ExpectedException.none(); 21 | Claims expected; 22 | String signedJWT; 23 | Key secret; 24 | Key bogusSecret; 25 | 26 | private static Claims claims() { 27 | return Jwts.claims() 28 | .setIssuer("griff.io") 29 | .setSubject("test.work") 30 | .setAudience("localhost"); 31 | } 32 | 33 | @Before 34 | public void fixture() throws Exception { 35 | secret = MacProvider.generateKey(); 36 | bogusSecret = MacProvider.generateKey(); 37 | expected = claims(); 38 | signedJWT = Jwts.builder().setClaims(expected).signWith(SignatureAlgorithm.HS512, secret).compact(); 39 | } 40 | 41 | @Test 42 | public void jwt_bogus_secret_key_claims_are_not_valid() throws Exception { 43 | thrown.expect(SignatureException.class); 44 | Jwts.parser().setSigningKey(bogusSecret).parseClaimsJws(signedJWT); 45 | } 46 | 47 | @Test 48 | public void jwt_secret_key_claims_verified() throws Exception { 49 | Jws actual = Jwts.parser().setSigningKey(secret).parseClaimsJws(signedJWT); 50 | Assert.assertEquals(expected.getIssuer(), actual.getBody().getIssuer()); 51 | Assert.assertEquals(expected.getSubject(), actual.getBody().getSubject()); 52 | Assert.assertEquals(expected.getAudience(), actual.getBody().getAudience()); 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /src/test/java/griffio/auth0/spring/WebContextTests.java: -------------------------------------------------------------------------------- 1 | package griffio.auth0.spring; 2 | 3 | import io.jsonwebtoken.Claims; 4 | import io.jsonwebtoken.Jwts; 5 | import io.jsonwebtoken.SignatureAlgorithm; 6 | import org.junit.Before; 7 | import org.junit.Test; 8 | import org.junit.runner.RunWith; 9 | import org.springframework.beans.factory.annotation.Value; 10 | import org.springframework.boot.test.context.SpringBootTest; 11 | import org.springframework.security.web.FilterChainProxy; 12 | import org.springframework.test.context.junit4.SpringRunner; 13 | import org.springframework.test.context.web.WebAppConfiguration; 14 | import org.springframework.test.web.servlet.MockMvc; 15 | import org.springframework.test.web.servlet.setup.MockMvcBuilders; 16 | import org.springframework.web.context.WebApplicationContext; 17 | 18 | import javax.annotation.Resource; 19 | import java.util.Base64; 20 | import java.util.Date; 21 | 22 | import static org.springframework.http.HttpHeaders.*; 23 | import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.get; 24 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.content; 25 | import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; 26 | 27 | @RunWith(SpringRunner.class) 28 | @SpringBootTest 29 | @WebAppConfiguration 30 | public class WebContextTests { 31 | 32 | @Resource 33 | private WebApplicationContext context; 34 | 35 | @Resource 36 | private FilterChainProxy defaultSecurityFilterChain; 37 | 38 | private MockMvc mvc; 39 | 40 | @Value(value = "${auth0.clientId}") 41 | private String clientId; 42 | 43 | @Value(value = "${auth0.clientSecret}") 44 | private String clientSecret; 45 | 46 | @Value(value = "${auth0.domain}") 47 | private String issuer; 48 | 49 | @Before 50 | public void setup() { 51 | 52 | mvc = MockMvcBuilders 53 | .webAppContextSetup(context) 54 | .addFilters(defaultSecurityFilterChain) 55 | .build(); 56 | } 57 | 58 | @Test 59 | public void handshake_is_allowed() throws Exception { 60 | mvc.perform(get("/handshake")).andExpect(status().isOk()).andExpect(content().string("OK")); 61 | } 62 | 63 | @Test 64 | public void handshake_is_not_allowed() throws Exception { 65 | mvc.perform(get("/authorised/handshake")).andExpect(status().isUnauthorized()); 66 | } 67 | 68 | @Test 69 | public void index_html_is_allowed() throws Exception { 70 | mvc.perform(get("/index.html")).andExpect(status().isOk()); 71 | } 72 | 73 | @Test 74 | public void favicon_is_allowed() throws Exception { 75 | mvc.perform(get("/favicon.ico")).andExpect(status().isOk()); 76 | } 77 | 78 | @Test 79 | public void authorised_handshake_is_allowed() throws Exception { 80 | 81 | Date now = new Date(); 82 | 83 | Claims claims = Jwts.claims() 84 | .setAudience(clientId) 85 | .setIssuer("griff.io") 86 | .setIssuedAt(now) 87 | .setNotBefore(now) 88 | .setSubject("test.work"); 89 | 90 | byte[] signingKey = Base64.getUrlDecoder().decode(clientSecret); 91 | 92 | String jwt = Jwts.builder().setClaims(claims).signWith(SignatureAlgorithm.HS256, signingKey).compact(); 93 | 94 | String bearerToken = String.format("Bearer %s", jwt); 95 | 96 | mvc.perform(get("/authorised/handshake") 97 | .header(AUTHORIZATION, bearerToken).header(ORIGIN, "example.com")) 98 | .andExpect(status().isOk()) 99 | .andExpect(content().string("OK")); 100 | } 101 | 102 | } -------------------------------------------------------------------------------- /src/test/resources/config/application.properties: -------------------------------------------------------------------------------- 1 | application.name=auth0 application 2 | application.version=1.0.0 3 | logging.file=build/test-griffio.log 4 | auth0.clientId=VHlwZSAob3IgcGFzdGUpIGhlcmUuLi4= 5 | auth0.clientSecret=1XM2OTJM9G85TmkyO1srxUwa9nB01XN9 6 | auth0.domain=example.auth0.com 7 | 8 | cors.allowed-origins=example.com --------------------------------------------------------------------------------