diff --git a/custom-login-authjs/README.md b/custom-login-authjs/README.md new file mode 100644 index 0000000..f23a9b4 --- /dev/null +++ b/custom-login-authjs/README.md @@ -0,0 +1,46 @@ +# Okta Spring Security & Custom Login Page with AuthJs Example + +This example shows you how to use the [Okta Spring Boot Library][] to login a user. The login is achieved through the [Authorization Code Flow][] using the [Okta Auth JS Library][], which gives you more control to customize the login experience within your app. After the user authenticates, they are redirected back to the application and a local cookie session is created. + + +## Prerequisites + +Before running this sample, you will need the following: + +* An Okta Developer Account, you can sign up for one at https://developer.okta.com/signup/. +* An Okta Application, configured for Web mode. This is done from the Okta Developer Console and you can find instructions [here][OIDC Web Application Setup Instructions]. When following the wizard, use the default properties. They are designed to work with our sample applications. + +## Running This Example + +There is a pom.xml at the root of this project, that exists to build all of the projects. Each project is independent and could be copied out of this repo as a primer for your own application. + +You also need to gather the following information from the Okta Developer Console: + +- **Client ID** and **Client Secret** - These can be found on the "General" tab of the Web application that you created earlier in the Okta Developer Console. + +- **Issuer** - This is the URL of the authorization server that will perform authentication. All Developer Accounts have a "default" authorization server. The issuer is a combination of your Org URL (found in the upper right of the console home page) and `/oauth2/default`. For example, `https://dev-1234.oktapreview.com/oauth2/default`. + +Plug these values into the `mvn` commands used to start the application. + +```bash +cd custom-login-authjs +mvn -Dokta.oauth2.issuer=https://{yourOktaDomain}/oauth2/default \ + -Dokta.oauth2.clientId={yourClientId} \ + -Dokta.oauth2.clientSecret={yourClientSecret} \ + -Dokta.client.orgUrl=https://{yourOktaDomain} +``` + +> **NOTE:** Putting secrets on the command line should ONLY be done for examples, do NOT do this in production. Instead update the projects `application.yml` + +Now navigate to http://localhost:8080 in your browser. + +If you see a home page that prompts you to login, then things are working! Clicking the **Login** button will render a custom login page, served by the Spring Boot application, that uses the [Okta Auth JS Library][] to perform authentication. + +You can login with the same account that you created when signing up for your Developer Org, or you can use a known username and password from your Okta Directory. + +> **NOTE:** If you are currently using your Developer Console, you already have a Single Sign-On (SSO) session for your Org. You will be automatically logged into your application as the same user that is using the Developer Console. You may want to use an incognito tab to test the flow from a blank slate. + +[Okta Spring Boot Library]: https://github.com/okta/okta-spring-boot +[OIDC Web Application Setup Instructions]: https://developer.okta.com/authentication-guide/implementing-authentication/auth-code#1-setting-up-your-application +[Authorization Code Flow]: https://developer.okta.com/authentication-guide/implementing-authentication/auth-code +[Okta Sign In Widget]: https://github.com/okta/okta-auth-js diff --git a/custom-login/mvnw b/custom-login-authjs/mvnw similarity index 100% rename from custom-login/mvnw rename to custom-login-authjs/mvnw diff --git a/custom-login/mvnw.cmd b/custom-login-authjs/mvnw.cmd similarity index 100% rename from custom-login/mvnw.cmd rename to custom-login-authjs/mvnw.cmd diff --git a/custom-login/pom.xml b/custom-login-authjs/pom.xml similarity index 97% rename from custom-login/pom.xml rename to custom-login-authjs/pom.xml index 205ab63..cc07932 100644 --- a/custom-login/pom.xml +++ b/custom-login-authjs/pom.xml @@ -24,12 +24,12 @@ com.example.okta - okta-spring-boot-oauth-code-flow-custom-login-example + okta-spring-boot-oauth-code-flow-custom-login-authjs-example 0.0.1-SNAPSHOT jar - Okta :: Samples :: Spring Boot :: Code Flow + Custom Login Page - Okta OAuth 2.0 w/ Spring Boot Example + Custom Login Page + Okta :: Samples :: Spring Boot :: Code Flow + Custom Login Page with AuthJs + Okta OAuth 2.0 w/ Spring Boot Example + Custom Login Page with AuthJs UTF-8 diff --git a/custom-login-authjs/src/main/java/com/okta/spring/example/CustomLoginCodeFlowAuthJsExampleApplication.java b/custom-login-authjs/src/main/java/com/okta/spring/example/CustomLoginCodeFlowAuthJsExampleApplication.java new file mode 100644 index 0000000..faddd92 --- /dev/null +++ b/custom-login-authjs/src/main/java/com/okta/spring/example/CustomLoginCodeFlowAuthJsExampleApplication.java @@ -0,0 +1,131 @@ +/* + * Copyright 2017 Okta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.okta.spring.example; + +import com.okta.spring.config.OktaOAuth2Properties; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; +import org.springframework.boot.autoconfigure.security.oauth2.client.EnableOAuth2Sso; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.ApplicationListener; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.annotation.Order; +import org.springframework.security.access.expression.method.MethodSecurityExpressionHandler; +import org.springframework.security.authentication.event.AuthenticationSuccessEvent; +import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity; +import org.springframework.security.config.annotation.method.configuration.GlobalMethodSecurityConfiguration; +import org.springframework.security.config.annotation.web.builders.HttpSecurity; +import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter; +import org.springframework.security.oauth2.client.OAuth2ClientContext; +import org.springframework.security.oauth2.client.OAuth2RestTemplate; +import org.springframework.security.oauth2.client.filter.OAuth2ClientAuthenticationProcessingFilter; +import org.springframework.security.oauth2.client.token.grant.code.AuthorizationCodeResourceDetails; +import org.springframework.security.oauth2.provider.expression.OAuth2MethodSecurityExpressionHandler; +import org.springframework.security.oauth2.provider.token.ResourceServerTokenServices; +import org.springframework.security.web.authentication.LoginUrlAuthenticationEntryPoint; +import org.springframework.security.web.authentication.preauth.AbstractPreAuthenticatedProcessingFilter; + +import javax.servlet.Filter; + +/** + * This example renders a custom login page (hosted within this application). You can use a standard login with less + * code (if you don't need to customize the login page) see the 'basic' example at the root of this repository. + */ +@SpringBootApplication +@EnableOAuth2Sso +public class CustomLoginCodeFlowAuthJsExampleApplication { + + private final Logger logger = LoggerFactory.getLogger(CustomLoginCodeFlowAuthJsExampleApplication.class); + + public static void main(String[] args) { + SpringApplication.run(CustomLoginCodeFlowAuthJsExampleApplication.class, args); + } + + /** + * Enable the use of {@link org.springframework.security.access.prepost.PreAuthorize PreAuthorize} annotation + * and OAuth expressions like {code}#oauth2.hasScope('email'){code}. + */ + @EnableGlobalMethodSecurity(prePostEnabled = true) + protected static class GlobalSecurityConfiguration extends GlobalMethodSecurityConfiguration { + @Override + protected MethodSecurityExpressionHandler createExpressionHandler() { + return new OAuth2MethodSecurityExpressionHandler(); + } + } + + /** + * Create an ApplicationListener that listens for successful logins and simply just logs the principal name. + * @return a new listener + */ + @Bean + protected ApplicationListener authenticationSuccessEventApplicationListener() { + return event -> logger.info("Authentication Success with principal: {}", event.getAuthentication().getPrincipal()); + } + + @Bean + protected Filter oktaSsoFilter(ApplicationEventPublisher applicationEventPublisher, + OAuth2ClientContext oauth2ClientContext, + AuthorizationCodeResourceDetails authorizationCodeResourceDetails, + ResourceServerTokenServices tokenServices, + OktaOAuth2Properties oktaOAuth2Properties) { + + // There are a few package private classes the configure a OAuth2ClientAuthenticationProcessingFilter, in order + // to change how the login redirect works we need to copy a bit of that code here + OAuth2ClientAuthenticationProcessingFilter oktaFilter = new OAuth2ClientAuthenticationProcessingFilter(oktaOAuth2Properties.getRedirectUri()); + oktaFilter.setApplicationEventPublisher(applicationEventPublisher); + OAuth2RestTemplate oktaTemplate = new OAuth2RestTemplate(authorizationCodeResourceDetails, oauth2ClientContext); + oktaFilter.setRestTemplate(oktaTemplate); + oktaFilter.setTokenServices(tokenServices); + return oktaFilter; + } + + @Configuration + @Order(99) // Must be less then 100 in order to configure before OAuth2SsoDefaultConfiguration + static class OAuth2SecurityConfigurerAdapter extends WebSecurityConfigurerAdapter { + + private final Filter oktaSsoFilter; + + private final OktaOAuth2Properties oktaOAuth2Properties; + + OAuth2SecurityConfigurerAdapter(Filter oktaSsoFilter, OktaOAuth2Properties oktaOAuth2Properties) { + this.oktaSsoFilter = oktaSsoFilter; + this.oktaOAuth2Properties = oktaOAuth2Properties; + } + + @Override + protected void configure(HttpSecurity http) throws Exception { + http + // add our SSO Filter in place + .addFilterAfter(oktaSsoFilter, AbstractPreAuthenticatedProcessingFilter.class) + .exceptionHandling() + .authenticationEntryPoint(new LoginUrlAuthenticationEntryPoint(oktaOAuth2Properties.getRedirectUri())) + .accessDeniedHandler((req, res, e) -> res.sendRedirect("/403")) + + // allow anonymous users to access the root page + .and() + .authorizeRequests() + .antMatchers("/", "/login", "/css/**").permitAll() + .antMatchers("/**").authenticated() + + // send the user back to the root page when they logout + .and() + .logout().logoutSuccessUrl("/"); + } + } +} \ No newline at end of file diff --git a/custom-login-authjs/src/main/java/com/okta/spring/example/controllers/HomeController.java b/custom-login-authjs/src/main/java/com/okta/spring/example/controllers/HomeController.java new file mode 100644 index 0000000..94a2f74 --- /dev/null +++ b/custom-login-authjs/src/main/java/com/okta/spring/example/controllers/HomeController.java @@ -0,0 +1,14 @@ +package com.okta.spring.example.controllers; + +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +import java.security.Principal; + +@Controller +public class HomeController { + @GetMapping("/") + public String home() { + return "home"; + } +} \ No newline at end of file diff --git a/custom-login/src/main/java/com/okta/spring/example/controllers/LoginController.java b/custom-login-authjs/src/main/java/com/okta/spring/example/controllers/LoginController.java similarity index 100% rename from custom-login/src/main/java/com/okta/spring/example/controllers/LoginController.java rename to custom-login-authjs/src/main/java/com/okta/spring/example/controllers/LoginController.java diff --git a/custom-login/src/main/java/com/okta/spring/example/controllers/UserDetailsController.java b/custom-login-authjs/src/main/java/com/okta/spring/example/controllers/UserDetailsController.java similarity index 100% rename from custom-login/src/main/java/com/okta/spring/example/controllers/UserDetailsController.java rename to custom-login-authjs/src/main/java/com/okta/spring/example/controllers/UserDetailsController.java diff --git a/custom-login/src/main/resources/application.yml b/custom-login-authjs/src/main/resources/application.yml similarity index 100% rename from custom-login/src/main/resources/application.yml rename to custom-login-authjs/src/main/resources/application.yml diff --git a/custom-login/src/main/resources/static/css/okta.css b/custom-login-authjs/src/main/resources/static/css/okta.css similarity index 100% rename from custom-login/src/main/resources/static/css/okta.css rename to custom-login-authjs/src/main/resources/static/css/okta.css diff --git a/custom-login/src/main/resources/templates/403.html b/custom-login-authjs/src/main/resources/templates/403.html similarity index 100% rename from custom-login/src/main/resources/templates/403.html rename to custom-login-authjs/src/main/resources/templates/403.html diff --git a/custom-login/src/main/resources/templates/head.html b/custom-login-authjs/src/main/resources/templates/head.html similarity index 100% rename from custom-login/src/main/resources/templates/head.html rename to custom-login-authjs/src/main/resources/templates/head.html diff --git a/custom-login/src/main/resources/templates/home.html b/custom-login-authjs/src/main/resources/templates/home.html similarity index 100% rename from custom-login/src/main/resources/templates/home.html rename to custom-login-authjs/src/main/resources/templates/home.html diff --git a/custom-login-authjs/src/main/resources/templates/login.html b/custom-login-authjs/src/main/resources/templates/login.html new file mode 100644 index 0000000..ca0ed93 --- /dev/null +++ b/custom-login-authjs/src/main/resources/templates/login.html @@ -0,0 +1,70 @@ + + + + Login + + + + + + + + +
+

Login

+
+ + + + + + + +
+
+ + + + diff --git a/custom-login/src/main/resources/templates/menu.html b/custom-login-authjs/src/main/resources/templates/menu.html similarity index 100% rename from custom-login/src/main/resources/templates/menu.html rename to custom-login-authjs/src/main/resources/templates/menu.html diff --git a/custom-login/src/main/resources/templates/userProfile.html b/custom-login-authjs/src/main/resources/templates/userProfile.html similarity index 100% rename from custom-login/src/main/resources/templates/userProfile.html rename to custom-login-authjs/src/main/resources/templates/userProfile.html diff --git a/custom-login/src/test/resources/logback.xml b/custom-login-authjs/src/test/resources/logback.xml similarity index 100% rename from custom-login/src/test/resources/logback.xml rename to custom-login-authjs/src/test/resources/logback.xml diff --git a/custom-login/src/test/resources/package.json b/custom-login-authjs/src/test/resources/package.json similarity index 100% rename from custom-login/src/test/resources/package.json rename to custom-login-authjs/src/test/resources/package.json diff --git a/custom-login/src/test/resources/testRunner.yml b/custom-login-authjs/src/test/resources/testRunner.yml similarity index 100% rename from custom-login/src/test/resources/testRunner.yml rename to custom-login-authjs/src/test/resources/testRunner.yml diff --git a/custom-login/.mvn/wrapper/maven-wrapper.jar b/custom-login-osw/.mvn/wrapper/maven-wrapper.jar similarity index 100% rename from custom-login/.mvn/wrapper/maven-wrapper.jar rename to custom-login-osw/.mvn/wrapper/maven-wrapper.jar diff --git a/custom-login/.mvn/wrapper/maven-wrapper.properties b/custom-login-osw/.mvn/wrapper/maven-wrapper.properties similarity index 100% rename from custom-login/.mvn/wrapper/maven-wrapper.properties rename to custom-login-osw/.mvn/wrapper/maven-wrapper.properties diff --git a/custom-login/README.md b/custom-login-osw/README.md similarity index 97% rename from custom-login/README.md rename to custom-login-osw/README.md index 005e122..1d012e5 100644 --- a/custom-login/README.md +++ b/custom-login-osw/README.md @@ -1,4 +1,4 @@ -# Okta Spring Security & Custom Login Page Example +# Okta Spring Security & Custom Login Page with SignIn Widget Example This example shows you how to use the [Okta Spring Boot Library][] to login a user. The login is achieved through the [Authorization Code Flow][] using the [Okta Sign In Widget][], which gives you more control to customize the login experience within your app. After the user authenticates, they are redirected back to the application and a local cookie session is created. @@ -23,7 +23,7 @@ You also need to gather the following information from the Okta Developer Consol Plug these values into the `mvn` commands used to start the application. ```bash -cd custom-login +cd custom-login-osw mvn -Dokta.oauth2.issuer=https://{yourOktaDomain}/oauth2/default \ -Dokta.oauth2.clientId={yourClientId} \ -Dokta.oauth2.clientSecret={yourClientSecret} \ diff --git a/custom-login-osw/mvnw b/custom-login-osw/mvnw new file mode 100755 index 0000000..5bf251c --- /dev/null +++ b/custom-login-osw/mvnw @@ -0,0 +1,225 @@ +#!/bin/sh +# ---------------------------------------------------------------------------- +# Licensed to the Apache Software Foundation (ASF) under one +# or more contributor license agreements. See the NOTICE file +# distributed with this work for additional information +# regarding copyright ownership. The ASF licenses this file +# to you under the Apache License, Version 2.0 (the +# "License"); you may not use this file except in compliance +# with the License. You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, +# software distributed under the License is distributed on an +# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +# KIND, either express or implied. See the License for the +# specific language governing permissions and limitations +# under the License. +# ---------------------------------------------------------------------------- + +# ---------------------------------------------------------------------------- +# Maven2 Start Up Batch script +# +# Required ENV vars: +# ------------------ +# JAVA_HOME - location of a JDK home dir +# +# Optional ENV vars +# ----------------- +# M2_HOME - location of maven2's installed home dir +# MAVEN_OPTS - parameters passed to the Java VM when running Maven +# e.g. to debug Maven itself, use +# set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +# MAVEN_SKIP_RC - flag to disable loading of mavenrc files +# ---------------------------------------------------------------------------- + +if [ -z "$MAVEN_SKIP_RC" ] ; then + + if [ -f /etc/mavenrc ] ; then + . /etc/mavenrc + fi + + if [ -f "$HOME/.mavenrc" ] ; then + . "$HOME/.mavenrc" + fi + +fi + +# OS specific support. $var _must_ be set to either true or false. +cygwin=false; +darwin=false; +mingw=false +case "`uname`" in + CYGWIN*) cygwin=true ;; + MINGW*) mingw=true;; + Darwin*) darwin=true + # Use /usr/libexec/java_home if available, otherwise fall back to /Library/Java/Home + # See https://developer.apple.com/library/mac/qa/qa1170/_index.html + if [ -z "$JAVA_HOME" ]; then + if [ -x "/usr/libexec/java_home" ]; then + export JAVA_HOME="`/usr/libexec/java_home`" + else + export JAVA_HOME="/Library/Java/Home" + fi + fi + ;; +esac + +if [ -z "$JAVA_HOME" ] ; then + if [ -r /etc/gentoo-release ] ; then + JAVA_HOME=`java-config --jre-home` + fi +fi + +if [ -z "$M2_HOME" ] ; then + ## resolve links - $0 may be a link to maven's home + PRG="$0" + + # need this for relative symlinks + while [ -h "$PRG" ] ; do + ls=`ls -ld "$PRG"` + link=`expr "$ls" : '.*-> \(.*\)$'` + if expr "$link" : '/.*' > /dev/null; then + PRG="$link" + else + PRG="`dirname "$PRG"`/$link" + fi + done + + saveddir=`pwd` + + M2_HOME=`dirname "$PRG"`/.. + + # make it fully qualified + M2_HOME=`cd "$M2_HOME" && pwd` + + cd "$saveddir" + # echo Using m2 at $M2_HOME +fi + +# For Cygwin, ensure paths are in UNIX format before anything is touched +if $cygwin ; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --unix "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --unix "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --unix "$CLASSPATH"` +fi + +# For Migwn, ensure paths are in UNIX format before anything is touched +if $mingw ; then + [ -n "$M2_HOME" ] && + M2_HOME="`(cd "$M2_HOME"; pwd)`" + [ -n "$JAVA_HOME" ] && + JAVA_HOME="`(cd "$JAVA_HOME"; pwd)`" + # TODO classpath? +fi + +if [ -z "$JAVA_HOME" ]; then + javaExecutable="`which javac`" + if [ -n "$javaExecutable" ] && ! [ "`expr \"$javaExecutable\" : '\([^ ]*\)'`" = "no" ]; then + # readlink(1) is not available as standard on Solaris 10. + readLink=`which readlink` + if [ ! `expr "$readLink" : '\([^ ]*\)'` = "no" ]; then + if $darwin ; then + javaHome="`dirname \"$javaExecutable\"`" + javaExecutable="`cd \"$javaHome\" && pwd -P`/javac" + else + javaExecutable="`readlink -f \"$javaExecutable\"`" + fi + javaHome="`dirname \"$javaExecutable\"`" + javaHome=`expr "$javaHome" : '\(.*\)/bin'` + JAVA_HOME="$javaHome" + export JAVA_HOME + fi + fi +fi + +if [ -z "$JAVACMD" ] ; then + if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD="$JAVA_HOME/jre/sh/java" + else + JAVACMD="$JAVA_HOME/bin/java" + fi + else + JAVACMD="`which java`" + fi +fi + +if [ ! -x "$JAVACMD" ] ; then + echo "Error: JAVA_HOME is not defined correctly." >&2 + echo " We cannot execute $JAVACMD" >&2 + exit 1 +fi + +if [ -z "$JAVA_HOME" ] ; then + echo "Warning: JAVA_HOME environment variable is not set." +fi + +CLASSWORLDS_LAUNCHER=org.codehaus.plexus.classworlds.launcher.Launcher + +# traverses directory structure from process work directory to filesystem root +# first directory with .mvn subdirectory is considered project base directory +find_maven_basedir() { + + if [ -z "$1" ] + then + echo "Path not specified to find_maven_basedir" + return 1 + fi + + basedir="$1" + wdir="$1" + while [ "$wdir" != '/' ] ; do + if [ -d "$wdir"/.mvn ] ; then + basedir=$wdir + break + fi + # workaround for JBEAP-8937 (on Solaris 10/Sparc) + if [ -d "${wdir}" ]; then + wdir=`cd "$wdir/.."; pwd` + fi + # end of workaround + done + echo "${basedir}" +} + +# concatenates all lines of a file +concat_lines() { + if [ -f "$1" ]; then + echo "$(tr -s '\n' ' ' < "$1")" + fi +} + +BASE_DIR=`find_maven_basedir "$(pwd)"` +if [ -z "$BASE_DIR" ]; then + exit 1; +fi + +export MAVEN_PROJECTBASEDIR=${MAVEN_BASEDIR:-"$BASE_DIR"} +echo $MAVEN_PROJECTBASEDIR +MAVEN_OPTS="$(concat_lines "$MAVEN_PROJECTBASEDIR/.mvn/jvm.config") $MAVEN_OPTS" + +# For Cygwin, switch paths to Windows format before running java +if $cygwin; then + [ -n "$M2_HOME" ] && + M2_HOME=`cygpath --path --windows "$M2_HOME"` + [ -n "$JAVA_HOME" ] && + JAVA_HOME=`cygpath --path --windows "$JAVA_HOME"` + [ -n "$CLASSPATH" ] && + CLASSPATH=`cygpath --path --windows "$CLASSPATH"` + [ -n "$MAVEN_PROJECTBASEDIR" ] && + MAVEN_PROJECTBASEDIR=`cygpath --path --windows "$MAVEN_PROJECTBASEDIR"` +fi + +WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +exec "$JAVACMD" \ + $MAVEN_OPTS \ + -classpath "$MAVEN_PROJECTBASEDIR/.mvn/wrapper/maven-wrapper.jar" \ + "-Dmaven.home=${M2_HOME}" "-Dmaven.multiModuleProjectDirectory=${MAVEN_PROJECTBASEDIR}" \ + ${WRAPPER_LAUNCHER} $MAVEN_CONFIG "$@" diff --git a/custom-login-osw/mvnw.cmd b/custom-login-osw/mvnw.cmd new file mode 100644 index 0000000..019bd74 --- /dev/null +++ b/custom-login-osw/mvnw.cmd @@ -0,0 +1,143 @@ +@REM ---------------------------------------------------------------------------- +@REM Licensed to the Apache Software Foundation (ASF) under one +@REM or more contributor license agreements. See the NOTICE file +@REM distributed with this work for additional information +@REM regarding copyright ownership. The ASF licenses this file +@REM to you under the Apache License, Version 2.0 (the +@REM "License"); you may not use this file except in compliance +@REM with the License. You may obtain a copy of the License at +@REM +@REM http://www.apache.org/licenses/LICENSE-2.0 +@REM +@REM Unless required by applicable law or agreed to in writing, +@REM software distributed under the License is distributed on an +@REM "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY +@REM KIND, either express or implied. See the License for the +@REM specific language governing permissions and limitations +@REM under the License. +@REM ---------------------------------------------------------------------------- + +@REM ---------------------------------------------------------------------------- +@REM Maven2 Start Up Batch script +@REM +@REM Required ENV vars: +@REM JAVA_HOME - location of a JDK home dir +@REM +@REM Optional ENV vars +@REM M2_HOME - location of maven2's installed home dir +@REM MAVEN_BATCH_ECHO - set to 'on' to enable the echoing of the batch commands +@REM MAVEN_BATCH_PAUSE - set to 'on' to wait for a key stroke before ending +@REM MAVEN_OPTS - parameters passed to the Java VM when running Maven +@REM e.g. to debug Maven itself, use +@REM set MAVEN_OPTS=-Xdebug -Xrunjdwp:transport=dt_socket,server=y,suspend=y,address=8000 +@REM MAVEN_SKIP_RC - flag to disable loading of mavenrc files +@REM ---------------------------------------------------------------------------- + +@REM Begin all REM lines with '@' in case MAVEN_BATCH_ECHO is 'on' +@echo off +@REM enable echoing my setting MAVEN_BATCH_ECHO to 'on' +@if "%MAVEN_BATCH_ECHO%" == "on" echo %MAVEN_BATCH_ECHO% + +@REM set %HOME% to equivalent of $HOME +if "%HOME%" == "" (set "HOME=%HOMEDRIVE%%HOMEPATH%") + +@REM Execute a user defined script before this one +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPre +@REM check for pre script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_pre.bat" call "%HOME%\mavenrc_pre.bat" +if exist "%HOME%\mavenrc_pre.cmd" call "%HOME%\mavenrc_pre.cmd" +:skipRcPre + +@setlocal + +set ERROR_CODE=0 + +@REM To isolate internal variables from possible post scripts, we use another setlocal +@setlocal + +@REM ==== START VALIDATION ==== +if not "%JAVA_HOME%" == "" goto OkJHome + +echo. +echo Error: JAVA_HOME not found in your environment. >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +:OkJHome +if exist "%JAVA_HOME%\bin\java.exe" goto init + +echo. +echo Error: JAVA_HOME is set to an invalid directory. >&2 +echo JAVA_HOME = "%JAVA_HOME%" >&2 +echo Please set the JAVA_HOME variable in your environment to match the >&2 +echo location of your Java installation. >&2 +echo. +goto error + +@REM ==== END VALIDATION ==== + +:init + +@REM Find the project base dir, i.e. the directory that contains the folder ".mvn". +@REM Fallback to current working directory if not found. + +set MAVEN_PROJECTBASEDIR=%MAVEN_BASEDIR% +IF NOT "%MAVEN_PROJECTBASEDIR%"=="" goto endDetectBaseDir + +set EXEC_DIR=%CD% +set WDIR=%EXEC_DIR% +:findBaseDir +IF EXIST "%WDIR%"\.mvn goto baseDirFound +cd .. +IF "%WDIR%"=="%CD%" goto baseDirNotFound +set WDIR=%CD% +goto findBaseDir + +:baseDirFound +set MAVEN_PROJECTBASEDIR=%WDIR% +cd "%EXEC_DIR%" +goto endDetectBaseDir + +:baseDirNotFound +set MAVEN_PROJECTBASEDIR=%EXEC_DIR% +cd "%EXEC_DIR%" + +:endDetectBaseDir + +IF NOT EXIST "%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config" goto endReadAdditionalConfig + +@setlocal EnableExtensions EnableDelayedExpansion +for /F "usebackq delims=" %%a in ("%MAVEN_PROJECTBASEDIR%\.mvn\jvm.config") do set JVM_CONFIG_MAVEN_PROPS=!JVM_CONFIG_MAVEN_PROPS! %%a +@endlocal & set JVM_CONFIG_MAVEN_PROPS=%JVM_CONFIG_MAVEN_PROPS% + +:endReadAdditionalConfig + +SET MAVEN_JAVA_EXE="%JAVA_HOME%\bin\java.exe" + +set WRAPPER_JAR="%MAVEN_PROJECTBASEDIR%\.mvn\wrapper\maven-wrapper.jar" +set WRAPPER_LAUNCHER=org.apache.maven.wrapper.MavenWrapperMain + +%MAVEN_JAVA_EXE% %JVM_CONFIG_MAVEN_PROPS% %MAVEN_OPTS% %MAVEN_DEBUG_OPTS% -classpath %WRAPPER_JAR% "-Dmaven.multiModuleProjectDirectory=%MAVEN_PROJECTBASEDIR%" %WRAPPER_LAUNCHER% %MAVEN_CONFIG% %* +if ERRORLEVEL 1 goto error +goto end + +:error +set ERROR_CODE=1 + +:end +@endlocal & set ERROR_CODE=%ERROR_CODE% + +if not "%MAVEN_SKIP_RC%" == "" goto skipRcPost +@REM check for post script, once with legacy .bat ending and once with .cmd ending +if exist "%HOME%\mavenrc_post.bat" call "%HOME%\mavenrc_post.bat" +if exist "%HOME%\mavenrc_post.cmd" call "%HOME%\mavenrc_post.cmd" +:skipRcPost + +@REM pause the script if MAVEN_BATCH_PAUSE is set to 'on' +if "%MAVEN_BATCH_PAUSE%" == "on" pause + +if "%MAVEN_TERMINATE_CMD%" == "on" exit %ERROR_CODE% + +exit /B %ERROR_CODE% diff --git a/custom-login-osw/pom.xml b/custom-login-osw/pom.xml new file mode 100644 index 0000000..20e5e89 --- /dev/null +++ b/custom-login-osw/pom.xml @@ -0,0 +1,226 @@ + + + + 4.0.0 + + + org.springframework.boot + spring-boot-starter-parent + 2.0.5.RELEASE + + + com.example.okta + okta-spring-boot-oauth-code-flow-custom-login-osw-example + 0.0.1-SNAPSHOT + jar + + Okta :: Samples :: Spring Boot :: Code Flow + Custom Login Page with Okta SignIn Widget + Okta OAuth 2.0 w/ Spring Boot Example + Custom Login Page with Okta SignIn Widget + + + UTF-8 + UTF-8 + 1.8 + + + + + com.okta.spring + okta-spring-boot-starter + 0.6.1 + + + org.springframework.security.oauth.boot + spring-security-oauth2-autoconfigure + 2.0.2.RELEASE + + + org.springframework.security + spring-security-oauth2-client + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.security.oauth + spring-security-oauth2 + 2.2.0.RELEASE + + + org.springframework.boot + spring-boot-starter-security + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.thymeleaf.extras + thymeleaf-extras-springsecurity4 + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + org.springframework.boot + spring-boot-devtools + runtime + + + + + + org.slf4j + jcl-over-slf4j + runtime + + + ch.qos.logback + logback-classic + runtime + + + + + com.okta.oidc.tck + okta-oidc-tck + 0.4.0 + test + + + + + + spring-boot:run + + + org.springframework.boot + spring-boot-maven-plugin + + false + + + + + + + + + okta-tck + + + v8.1.2 + + + + + + + org.apache.maven.plugins + maven-failsafe-plugin + 2.20.1 + + + com.okta.oidc.tck:okta-oidc-tck + + + true + + + + + org.apache.maven.plugins + maven-dependency-plugin + 3.0.2 + + + unpack + package + + unpack + + + + + + com.okta.oidc.tck + okta-oidc-tck + e2e + zip + ${project.build.testOutputDirectory}/ + + + + com.okta.oidc.tck + okta-oidc-tck + ${project.build.directory} + tck-keystore.jks + tck-keystore.jks + + + false + true + + + + + + + com.github.eirslett + frontend-maven-plugin + 1.4 + + ${node.version} + ${project.build.testOutputDirectory} + + + + install node + verify + + install-node-and-npm + + + + npm install + verify + + npm + + + install + + + + npm test + verify + + npm + + + test + + + + + + + + + diff --git a/custom-login/src/main/java/com/okta/spring/example/HostedLoginCodeFlowExampleApplication.java b/custom-login-osw/src/main/java/com/okta/spring/example/CustomLoginCodeFlowOSWExampleApplication.java similarity index 96% rename from custom-login/src/main/java/com/okta/spring/example/HostedLoginCodeFlowExampleApplication.java rename to custom-login-osw/src/main/java/com/okta/spring/example/CustomLoginCodeFlowOSWExampleApplication.java index a12f349..cca42aa 100644 --- a/custom-login/src/main/java/com/okta/spring/example/HostedLoginCodeFlowExampleApplication.java +++ b/custom-login-osw/src/main/java/com/okta/spring/example/CustomLoginCodeFlowOSWExampleApplication.java @@ -49,12 +49,12 @@ */ @SpringBootApplication @EnableOAuth2Sso -public class HostedLoginCodeFlowExampleApplication { +public class CustomLoginCodeFlowOSWExampleApplication { - private final Logger logger = LoggerFactory.getLogger(HostedLoginCodeFlowExampleApplication.class); + private final Logger logger = LoggerFactory.getLogger(CustomLoginCodeFlowOSWExampleApplication.class); public static void main(String[] args) { - SpringApplication.run(HostedLoginCodeFlowExampleApplication.class, args); + SpringApplication.run(CustomLoginCodeFlowOSWExampleApplication.class, args); } /** diff --git a/custom-login/src/main/java/com/okta/spring/example/controllers/HomeController.java b/custom-login-osw/src/main/java/com/okta/spring/example/controllers/HomeController.java similarity index 100% rename from custom-login/src/main/java/com/okta/spring/example/controllers/HomeController.java rename to custom-login-osw/src/main/java/com/okta/spring/example/controllers/HomeController.java diff --git a/custom-login-osw/src/main/java/com/okta/spring/example/controllers/LoginController.java b/custom-login-osw/src/main/java/com/okta/spring/example/controllers/LoginController.java new file mode 100644 index 0000000..95ec835 --- /dev/null +++ b/custom-login-osw/src/main/java/com/okta/spring/example/controllers/LoginController.java @@ -0,0 +1,78 @@ +/* + * Copyright 2017 Okta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package com.okta.spring.example.controllers; + +import com.okta.spring.config.OktaClientProperties; +import com.okta.spring.config.OktaOAuth2Properties; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RequestParam; +import org.springframework.web.servlet.ModelAndView; + +import javax.servlet.http.HttpServletRequest; + +@Controller +public class LoginController { + + private static final String STATE = "state"; + private static final String SCOPES = "scopes"; + private static final String OKTA_BASE_URL = "oktaBaseUrl"; + private static final String OKTA_CLIENT_ID = "oktaClientId"; + private static final String REDIRECT_URI = "redirectUri"; + private static final String ISSUER_URI = "issuerUri"; + + private final OktaOAuth2Properties oktaOAuth2Properties; + + private final OktaClientProperties oktaClientProperties; + + public LoginController(OktaOAuth2Properties oktaOAuth2Properties, OktaClientProperties oktaClientProperties) { + this.oktaOAuth2Properties = oktaOAuth2Properties; + this.oktaClientProperties = oktaClientProperties; + } + + @GetMapping(value = "/login") + public ModelAndView login(HttpServletRequest request, + @RequestParam(name = "state", required = false) String state) { + + // if we don't have the state parameter redirect + if (state == null) { + return new ModelAndView("redirect:" + oktaOAuth2Properties.getRedirectUri()); + } + + // configuration for Okta Signin Widget + ModelAndView mav = new ModelAndView("login"); + mav.addObject(STATE, state); + mav.addObject(SCOPES, oktaOAuth2Properties.getScopes()); + mav.addObject(OKTA_BASE_URL, oktaClientProperties.getOrgUrl()); + mav.addObject(OKTA_CLIENT_ID, oktaOAuth2Properties.getClientId()); + mav.addObject(REDIRECT_URI, + request.getScheme() + "://" + request.getServerName() + ":" + request.getServerPort() + + request.getContextPath() + oktaOAuth2Properties.getRedirectUri() + ); + mav.addObject(ISSUER_URI, oktaOAuth2Properties.getIssuer()); + return mav; + } + + @GetMapping("/post-logout") + public String logout() { + return "logout"; + } + + @GetMapping("/403") + public String error403() { + return "403"; + } +} \ No newline at end of file diff --git a/custom-login-osw/src/main/java/com/okta/spring/example/controllers/UserDetailsController.java b/custom-login-osw/src/main/java/com/okta/spring/example/controllers/UserDetailsController.java new file mode 100644 index 0000000..9ee7000 --- /dev/null +++ b/custom-login-osw/src/main/java/com/okta/spring/example/controllers/UserDetailsController.java @@ -0,0 +1,18 @@ +package com.okta.spring.example.controllers; + +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.security.oauth2.provider.OAuth2Authentication; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.servlet.ModelAndView; + +import java.util.Collections; + +@Controller +public class UserDetailsController { + @GetMapping("/profile") + @PreAuthorize("#oauth2.hasScope('profile')") + public ModelAndView userDetails(OAuth2Authentication authentication) { + return new ModelAndView("userProfile" , Collections.singletonMap("details", authentication.getUserAuthentication().getDetails())); + } +} \ No newline at end of file diff --git a/custom-login-osw/src/main/resources/application.yml b/custom-login-osw/src/main/resources/application.yml new file mode 100644 index 0000000..2ff4336 --- /dev/null +++ b/custom-login-osw/src/main/resources/application.yml @@ -0,0 +1,14 @@ +server: + port: 8080 + servlet: + session: + tracking-modes: cookie +okta: + oauth2: + redirectUri: /authorization-code/callback + +security: + oauth2: + client: + # Redirect to your local page and render the widget + userAuthorizationUri: http://localhost:${server.port}/login \ No newline at end of file diff --git a/custom-login-osw/src/main/resources/static/css/okta.css b/custom-login-osw/src/main/resources/static/css/okta.css new file mode 100644 index 0000000..99d083b --- /dev/null +++ b/custom-login-osw/src/main/resources/static/css/okta.css @@ -0,0 +1,23 @@ +/* + * Copyright 2017 Okta, Inc. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +body.login { + background-color: #f9f9f9; +} + +#okta-sign-in { + min-height: 0 !important; +} + diff --git a/custom-login-osw/src/main/resources/templates/403.html b/custom-login-osw/src/main/resources/templates/403.html new file mode 100644 index 0000000..4130c38 --- /dev/null +++ b/custom-login-osw/src/main/resources/templates/403.html @@ -0,0 +1,36 @@ + + + + User Details + + + + +
+
+
+
+ +

Unauthorized

+ Home +
+
+
+ + \ No newline at end of file diff --git a/custom-login-osw/src/main/resources/templates/head.html b/custom-login-osw/src/main/resources/templates/head.html new file mode 100644 index 0000000..9fd5458 --- /dev/null +++ b/custom-login-osw/src/main/resources/templates/head.html @@ -0,0 +1,35 @@ + + + + + + + + + + + + + + + +

Nothing to see here, move along.

+ + \ No newline at end of file diff --git a/custom-login-osw/src/main/resources/templates/home.html b/custom-login-osw/src/main/resources/templates/home.html new file mode 100644 index 0000000..3792f47 --- /dev/null +++ b/custom-login-osw/src/main/resources/templates/home.html @@ -0,0 +1,45 @@ + + + + User Details + + + +
+ +
+

Custom Login + Spring Boot Example

+ +
+

Hello!

+

If you're viewing this page then you have successfully configured and started this example server.

+

This example shows you how to use the Okta Spring Boot Starter to add the Authorization Code Flow to your application.

+

When you click the login button below, you will be redirected to the login page on your Okta org. After you authenticate, you will be returned to this application.

+
+ +
+

Welcome back, Joe Coder!

+

You have successfully authenticated against your Okta org, and have been redirected back to this application.

+

Visit the My Profile page in this application to view the information retrieved with your OAuth Access Token.

+
+ +
+ +
+
+ + diff --git a/custom-login-osw/src/main/resources/templates/login.html b/custom-login-osw/src/main/resources/templates/login.html new file mode 100644 index 0000000..761a84d --- /dev/null +++ b/custom-login-osw/src/main/resources/templates/login.html @@ -0,0 +1,53 @@ + + + + Login + + + + + + + + + +
+ + + + diff --git a/custom-login-osw/src/main/resources/templates/menu.html b/custom-login-osw/src/main/resources/templates/menu.html new file mode 100644 index 0000000..a8a99b3 --- /dev/null +++ b/custom-login-osw/src/main/resources/templates/menu.html @@ -0,0 +1,34 @@ + + + + + + + + + \ No newline at end of file diff --git a/custom-login-osw/src/main/resources/templates/userProfile.html b/custom-login-osw/src/main/resources/templates/userProfile.html new file mode 100644 index 0000000..0051944 --- /dev/null +++ b/custom-login-osw/src/main/resources/templates/userProfile.html @@ -0,0 +1,49 @@ + + + + User Details + + + +
+ +
+ +
+

My Profile

+

Hello, Joe Coder. Below is the information that was read with your Access Token. +

+

This route is protected with the annotation @PreAuthorize("#oauth2.hasScope('profile')"), which will ensure that this page cannot be accessed until you have authenticated, and have the scope profile.

+
+ + + + + + + + + + + + + + +
ClaimValue
KeyValue
+
+ + \ No newline at end of file diff --git a/custom-login-osw/src/test/resources/logback.xml b/custom-login-osw/src/test/resources/logback.xml new file mode 100644 index 0000000..9f539e5 --- /dev/null +++ b/custom-login-osw/src/test/resources/logback.xml @@ -0,0 +1,31 @@ + + + + + + + + %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n + + + + + + + + \ No newline at end of file diff --git a/custom-login-osw/src/test/resources/package.json b/custom-login-osw/src/test/resources/package.json new file mode 100644 index 0000000..3210639 --- /dev/null +++ b/custom-login-osw/src/test/resources/package.json @@ -0,0 +1,19 @@ +{ + "name": "@okta/samples-java-spring-custom-login-tests", + "version": "0.0.2", + "scripts": { + "pretest": "webdriver-manager update --gecko false", + "test:protractor": "protractor e2e-tests/custom-login/conf.js", + "test": "npm run test:protractor", + "custom-login-server": "mvn -f ../../pom.xml -Dokta.oauth2.localTokenValidation=false" + }, + "devDependencies": { + "dotenv": "^5.0.1", + "find-process": "^1.1.0", + "forever-monitor": "^1.7.1", + "jasmine-reporters": "^2.2.0", + "platform": "^1.3.5", + "protractor": "^5.1.0", + "wait-on": "^2.0.2" + } +} \ No newline at end of file diff --git a/custom-login-osw/src/test/resources/testRunner.yml b/custom-login-osw/src/test/resources/testRunner.yml new file mode 100644 index 0000000..ecc7f14 --- /dev/null +++ b/custom-login-osw/src/test/resources/testRunner.yml @@ -0,0 +1,44 @@ +# +# Copyright 2018 Okta, Inc. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +scenarios: + custom-code-flow-local-validation: + disabledTests: + - respondWithCode + - invalidSignatureAccessTokenJwtTest + - noAuthCodeTest + command: ./mvnw + args: + - -Dserver.port=${applicationPort} + - -Dokta.oauth2.issuer=https://localhost:${mockHttpsPort}/oauth2/default + - -Dokta.oauth2.clientId=OOICU812 + - -Dokta.oauth2.clientSecret=VERY_SECRET + - -Dserver.session.trackingModes=cookie + - -Djavax.net.ssl.trustStore=target/tck-keystore.jks + - --batch-mode + + custom-code-flow-remote-validation: + disabledTests: + - respondWithCode + command: ./mvnw + args: + - -Dserver.port=${applicationPort} + - -Dokta.oauth2.issuer=https://localhost:${mockHttpsPort}/oauth2/default + - -Dokta.oauth2.clientId=OOICU812 + - -Dokta.oauth2.clientSecret=VERY_SECRET + - -Dserver.session.trackingModes=cookie + - -Dokta.oauth2.localTokenValidation=false + - -Djavax.net.ssl.trustStore=target/tck-keystore.jks + - --batch-mode \ No newline at end of file diff --git a/custom-login/src/main/resources/templates/login.html b/custom-login/src/main/resources/templates/login.html deleted file mode 100644 index dce75a0..0000000 --- a/custom-login/src/main/resources/templates/login.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - Login - - - - - - - - - -
- - - - - diff --git a/pom.xml b/pom.xml index 97015ca..2f5223a 100644 --- a/pom.xml +++ b/pom.xml @@ -27,7 +27,8 @@ okta-hosted-login - custom-login + custom-login-osw + custom-login-authjs front-end resource-server