Browse Source

Update web ui design for demo-authorizationserver

Issue gh-1196
Joe Grandja 2 years ago
parent
commit
4175effadd

+ 2 - 2
samples/demo-authorizationserver/samples-demo-authorizationserver.gradle

@@ -21,8 +21,8 @@ dependencies {
 	implementation "org.springframework.boot:spring-boot-starter-jdbc"
 	implementation project(":spring-security-oauth2-authorization-server")
 	implementation "org.webjars:webjars-locator-core"
-	implementation "org.webjars:bootstrap:3.4.1"
-	implementation "org.webjars:jquery:3.4.1"
+	implementation "org.webjars:bootstrap:5.2.3"
+	implementation "org.webjars:jquery:3.6.4"
 	runtimeOnly "com.h2database:h2"
 
 	testImplementation "org.springframework.boot:spring-boot-starter-test"

+ 10 - 8
samples/demo-authorizationserver/src/main/java/sample/web/DefaultErrorController.java

@@ -15,15 +15,13 @@
  */
 package sample.web;
 
-import java.util.Map;
-
 import jakarta.servlet.RequestDispatcher;
 import jakarta.servlet.http.HttpServletRequest;
 
 import org.springframework.boot.web.servlet.error.ErrorController;
 import org.springframework.stereotype.Controller;
+import org.springframework.ui.Model;
 import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.servlet.ModelAndView;
 
 /**
  * @author Steve Riesenberg
@@ -33,12 +31,16 @@ import org.springframework.web.servlet.ModelAndView;
 public class DefaultErrorController implements ErrorController {
 
 	@RequestMapping("/error")
-	public ModelAndView handleError(HttpServletRequest request) {
-		String message = getErrorMessage(request);
-		if (message.startsWith("[access_denied]")) {
-			return new ModelAndView("access-denied");
+	public String handleError(Model model, HttpServletRequest request) {
+		String errorMessage = getErrorMessage(request);
+		if (errorMessage.startsWith("[access_denied]")) {
+			model.addAttribute("errorTitle", "Access Denied");
+			model.addAttribute("errorMessage", "You have denied access.");
+		} else {
+			model.addAttribute("errorTitle", "Error");
+			model.addAttribute("errorMessage", errorMessage);
 		}
-		return new ModelAndView("error", Map.of("message", message));
+		return "error";
 	}
 
 	private String getErrorMessage(HttpServletRequest request) {

+ 3 - 3
samples/demo-authorizationserver/src/main/java/sample/web/DeviceController.java

@@ -31,17 +31,17 @@ public class DeviceController {
 		if (userCode != null) {
 			return "redirect:/oauth2/device_verification?user_code=" + userCode;
 		}
-		return "activate";
+		return "device-activate";
 	}
 
 	@GetMapping("/activated")
 	public String activated() {
-		return "activated";
+		return "device-activated";
 	}
 
 	@GetMapping(value = "/", params = "success")
 	public String success() {
-		return "activated";
+		return "device-activated";
 	}
 
 }

+ 32 - 0
samples/demo-authorizationserver/src/main/resources/static/assets/css/signin.css

@@ -0,0 +1,32 @@
+html,
+body {
+  height: 100%;
+}
+
+body {
+  display: flex;
+  align-items: start;
+  padding-top: 100px;
+  background-color: #f5f5f5;
+}
+
+.form-signin {
+  max-width: 330px;
+  padding: 15px;
+}
+
+.form-signin .form-floating:focus-within {
+  z-index: 2;
+}
+
+.form-signin input[type="username"] {
+  margin-bottom: -1px;
+  border-bottom-right-radius: 0;
+  border-bottom-left-radius: 0;
+}
+
+.form-signin input[type="password"] {
+  margin-bottom: 10px;
+  border-top-left-radius: 0;
+  border-top-right-radius: 0;
+}

+ 0 - 13
samples/demo-authorizationserver/src/main/resources/static/assets/css/style.css

@@ -1,13 +0,0 @@
-html, body, .container, .jumbotron {
-    height: 100%;
-}
-.jumbotron {
-    margin-bottom: 0;
-}
-.gap {
-    margin-top: 70px;
-}
-.code {
-    font-size: 2em;
-    letter-spacing: 2rem;
-}

BIN
samples/demo-authorizationserver/src/main/resources/static/assets/img/devices.png


BIN
samples/demo-authorizationserver/src/main/resources/static/assets/img/github.png


BIN
samples/demo-authorizationserver/src/main/resources/static/assets/img/google.png


+ 0 - 20
samples/demo-authorizationserver/src/main/resources/templates/access-denied.html

@@ -1,20 +0,0 @@
-<!DOCTYPE html>
-<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
-    <head>
-        <meta charset="utf-8">
-        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
-        <title>Spring Security Example</title>
-        <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.css" th:href="@{/webjars/bootstrap/css/bootstrap.css}" />
-        <link rel="stylesheet" href="/assets/css/style.css" th:href="@{/assets/css/style.css}" />
-    </head>
-    <body>
-        <div class="container">
-            <div class="row">
-                <div class="col-md-8">
-                    <h2>Access Denied</h2>
-                    <p>You have denied access.</p>
-                </div>
-            </div>
-        </div>
-    </body>
-</html>

+ 0 - 33
samples/demo-authorizationserver/src/main/resources/templates/activate.html

@@ -1,33 +0,0 @@
-<!DOCTYPE html>
-<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
-    <head>
-        <meta charset="utf-8">
-        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
-        <title>Device Grant Example</title>
-        <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.css" th:href="@{/webjars/bootstrap/css/bootstrap.css}" />
-        <link rel="stylesheet" href="/assets/css/style.css" th:href="@{/assets/css/style.css}" />
-    </head>
-    <body>
-        <div class="jumbotron">
-            <div class="container">
-                <div class="row">
-                    <div class="col-md-8">
-                        <form th:action="@{/oauth2/device_verification}" method="post">
-                            <h2>Device Activation</h2>
-                            <p>Enter the activation code to authorize the device.</p>
-                            <p class="gap">Activation Code</p>
-                            <div class="form-group">
-                                <label class="sr-only" for="user_code">Activation Code</label>
-                                <input type="text" class="form-control" id="user_code" name="user_code" placeholder="Activation Code" autofocus>
-                            </div>
-                            <button type="submit" class="btn btn-default">Submit</button>
-                        </form>
-                    </div>
-                    <div class="col-md-4">
-                        <img src="https://cdn.pixabay.com/photo/2017/07/03/15/20/technology-2468063_1280.png" class="img-responsive" alt="Devices">
-                    </div>
-                </div>
-            </div>
-        </div>
-    </body>
-</html>

+ 0 - 25
samples/demo-authorizationserver/src/main/resources/templates/activated.html

@@ -1,25 +0,0 @@
-<!DOCTYPE html>
-<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
-    <head>
-        <meta charset="utf-8">
-        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
-        <title>Device Grant Example</title>
-        <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.css" th:href="@{/webjars/bootstrap/css/bootstrap.css}" />
-        <link rel="stylesheet" href="/assets/css/style.css" th:href="@{/assets/css/style.css}" />
-    </head>
-    <body>
-        <div class="jumbotron">
-            <div class="container">
-                <div class="row">
-                    <div class="col-md-8">
-                        <h2>Success!</h2>
-                        <p>You have successfully activated your device. Please return to your device to continue.</p>
-                    </div>
-                    <div class="col-md-4">
-                        <img src="https://cdn.pixabay.com/photo/2017/07/03/15/20/technology-2468063_1280.png" class="img-responsive" alt="Devices">
-                    </div>
-                </div>
-            </div>
-        </div>
-    </body>
-</html>

+ 35 - 27
samples/demo-authorizationserver/src/main/resources/templates/consent.html

@@ -1,44 +1,48 @@
 <!DOCTYPE html>
-<html lang="en">
+<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
 <head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
-    <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.5.2/css/bootstrap.min.css"
-          integrity="sha384-JcKb8q3iqJ61gNV9KGb8thSsNjpSL0n8PARn9HuZOnIxN0hoP+VmmDGMN5t9UJ0Z" crossorigin="anonymous">
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1">
     <title>Custom consent page - Consent required</title>
-	<script>
-		function cancelConsent() {
-			document.consent_form.reset();
-			document.consent_form.submit();
-		}
-	</script>
+    <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.css" th:href="@{/webjars/bootstrap/css/bootstrap.css}" />
+    <script>
+        function cancelConsent() {
+            document.consent_form.reset();
+            document.consent_form.submit();
+        }
+    </script>
 </head>
 <body>
 <div class="container">
-    <div class="py-5">
+    <div class="row py-5">
         <h1 class="text-center text-primary">App permissions</h1>
     </div>
     <div class="row">
         <div class="col text-center">
             <p>
                 The application
-                <span class="font-weight-bold text-primary" th:text="${clientId}"></span>
+                <span class="fw-bold text-primary" th:text="${clientId}"></span>
                 wants to access your account
-                <span class="font-weight-bold" th:text="${principalName}"></span>
+                <span class="fw-bold" th:text="${principalName}"></span>
             </p>
         </div>
     </div>
     <div th:if="${userCode}" class="row">
         <div class="col text-center">
-            <p class="alert alert-warning">You have provided the code
-                <span class="font-weight-bold" th:text="${userCode}"></span>.
+            <p class="alert alert-warning">
+                You have provided the code
+                <span class="fw-bold" th:text="${userCode}"></span>.
                 Verify that this code matches what is shown on your device.
             </p>
         </div>
     </div>
     <div class="row pb-3">
-        <div class="col text-center"><p>The following permissions are requested by the above app.<br/>Please review
-            these and consent if you approve.</p></div>
+        <div class="col text-center">
+            <p>
+                The following permissions are requested by the above app.<br/>
+                Please review these and consent if you approve.
+            </p>
+        </div>
     </div>
     <div class="row">
         <div class="col text-center">
@@ -47,33 +51,37 @@
                 <input type="hidden" name="state" th:value="${state}">
                 <input th:if="${userCode}" type="hidden" name="user_code" th:value="${userCode}">
 
-                <div th:each="scope: ${scopes}" class="form-group form-check py-1">
+                <div th:each="scope: ${scopes}" class="form-check py-1">
                     <input class="form-check-input"
+                           style="float: none"
                            type="checkbox"
                            name="scope"
                            th:value="${scope.scope}"
                            th:id="${scope.scope}">
-                    <label class="form-check-label font-weight-bold" th:for="${scope.scope}" th:text="${scope.scope}"></label>
+                    <label class="form-check-label fw-bold px-2" th:for="${scope.scope}" th:text="${scope.scope}"></label>
                     <p class="text-primary" th:text="${scope.description}"></p>
                 </div>
 
-                <p th:if="${not #lists.isEmpty(previouslyApprovedScopes)}">You have already granted the following permissions to the above app:</p>
-                <div th:each="scope: ${previouslyApprovedScopes}" class="form-group form-check py-1">
+                <p th:if="${not #lists.isEmpty(previouslyApprovedScopes)}">
+                    You have already granted the following permissions to the above app:
+                </p>
+                <div th:each="scope: ${previouslyApprovedScopes}" class="form-check py-1">
                     <input class="form-check-input"
+                           style="float: none"
                            type="checkbox"
                            th:id="${scope.scope}"
                            disabled
                            checked>
-                    <label class="form-check-label font-weight-bold" th:for="${scope.scope}" th:text="${scope.scope}"></label>
+                    <label class="form-check-label fw-bold px-2" th:for="${scope.scope}" th:text="${scope.scope}"></label>
                     <p class="text-primary" th:text="${scope.description}"></p>
                 </div>
 
-                <div class="form-group pt-3">
+                <div class="pt-3">
                     <button class="btn btn-primary btn-lg" type="submit" id="submit-consent">
                         Submit Consent
                     </button>
                 </div>
-                <div class="form-group">
+                <div class="pt-3">
                     <button class="btn btn-link regular" type="button" id="cancel-consent" onclick="cancelConsent();">
                         Cancel
                     </button>
@@ -85,8 +93,8 @@
         <div class="col text-center">
             <p>
                 <small>
-                    Your consent to provide access is required.
-                    <br/>If you do not approve, click Cancel, in which case no information will be shared with the app.
+                    Your consent to provide access is required.<br/>
+                    If you do not approve, click Cancel, in which case no information will be shared with the app.
                 </small>
             </p>
         </div>

+ 33 - 0
samples/demo-authorizationserver/src/main/resources/templates/device-activate.html

@@ -0,0 +1,33 @@
+<!DOCTYPE html>
+<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
+<head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <title>Spring Authorization Server sample</title>
+    <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.css" th:href="@{/webjars/bootstrap/css/bootstrap.css}" />
+</head>
+<body>
+<div class="container">
+    <div class="row py-5">
+        <div class="col-md-5">
+            <h2>Device Activation</h2>
+            <p>Enter the activation code to authorize the device.</p>
+            <div class="mt-5">
+                <form th:action="@{/oauth2/device_verification}" method="post">
+                    <div class="mb-3">
+                        <label for="user_code" class="form-label">Activation Code</label>
+                        <input type="text" id="user_code" name="user_code" class="form-control" required autofocus>
+                    </div>
+                    <div class="mb-3">
+                        <button type="submit" class="btn btn-primary">Submit</button>
+                    </div>
+                </form>
+            </div>
+        </div>
+        <div class="col-md-7">
+            <img src="/assets/img/devices.png" th:src="@{/assets/img/devices.png}" class="img-responsive" alt="Devices">
+        </div>
+    </div>
+</div>
+</body>
+</html>

+ 25 - 0
samples/demo-authorizationserver/src/main/resources/templates/device-activated.html

@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
+<head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <title>Spring Authorization Server sample</title>
+    <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.css" th:href="@{/webjars/bootstrap/css/bootstrap.css}" />
+</head>
+<body>
+<div class="container">
+    <div class="row py-5">
+        <div class="col-md-5">
+            <h2 class="text-success">Success!</h2>
+            <p>
+                You have successfully activated your device.<br/>
+                Please return to your device to continue.
+            </p>
+        </div>
+        <div class="col-md-7">
+            <img src="/assets/img/devices.png" th:src="@{/assets/img/devices.png}" class="img-responsive" alt="Devices">
+        </div>
+    </div>
+</div>
+</body>
+</html>

+ 15 - 16
samples/demo-authorizationserver/src/main/resources/templates/error.html

@@ -1,20 +1,19 @@
 <!DOCTYPE html>
 <html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
-    <head>
-        <meta charset="utf-8">
-        <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
-        <title>Spring Security Example</title>
-        <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.css" th:href="@{/webjars/bootstrap/css/bootstrap.css}" />
-        <link rel="stylesheet" href="/assets/css/style.css" th:href="@{/assets/css/style.css}" />
-    </head>
-    <body>
-        <div class="container">
-            <div class="row">
-                <div class="col-md-8">
-                    <h2>Error</h2>
-                    <p th:text="${message}"></p>
-                </div>
-            </div>
+<head>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <title>Spring Authorization Server sample</title>
+    <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.css" th:href="@{/webjars/bootstrap/css/bootstrap.css}" />
+</head>
+<body>
+<div class="container">
+    <div class="row py-5">
+        <div class="col-md-6">
+            <h2 class="text-danger" th:text="${errorTitle}"></h2>
+            <p th:text="${errorMessage}"></p>
         </div>
-    </body>
+    </div>
+</div>
+</body>
 </html>

+ 28 - 27
samples/demo-authorizationserver/src/main/resources/templates/login.html

@@ -1,41 +1,42 @@
 <!DOCTYPE html>
-<html lang="en"
-      xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
+<html lang="en" xmlns="http://www.w3.org/1999/xhtml" xmlns:th="https://www.thymeleaf.org">
 <head>
-    <meta charset="utf-8">
-    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
-    <title>Spring Security Example</title>
-    <link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-beta/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-/Y6pD6FV/Vv2HJnA6t+vslU6fwYXjCFtcEpHbNJ0lyAFsXTsjBbfaDjzALeQsN6M" crossorigin="anonymous">
-    <link href="https://getbootstrap.com/docs/4.0/examples/signin/signin.css" rel="stylesheet" crossorigin="anonymous"/>
+    <meta charset="utf-8" />
+    <meta name="viewport" content="width=device-width, initial-scale=1">
+    <title>Spring Authorization Server sample</title>
+    <link rel="stylesheet" href="/webjars/bootstrap/css/bootstrap.css" th:href="@{/webjars/bootstrap/css/bootstrap.css}" />
+    <link rel="stylesheet" href="/assets/css/signin.css" th:href="@{/assets/css/signin.css}" />
 </head>
 <body>
 <div class="container">
-    <form class="form-signin" method="post" th:action="@{/login}">
+    <form class="form-signin w-100 m-auto" method="post" th:action="@{/login}">
         <div th:if="${param.error}" class="alert alert-danger" role="alert">
             Invalid username or password.
         </div>
         <div th:if="${param.logout}" class="alert alert-success" role="alert">
             You have been logged out.
         </div>
-        <h2 class="form-signin-heading">Sign In</h2>
-        <p>
-            <label for="username" class="sr-only">Username</label>
-            <input type="text" id="username" name="username" class="form-control" placeholder="Username" required autofocus>
-        </p>
-        <p>
-            <label for="password" class="sr-only">Password</label>
-            <input type="password" id="password" name="password" class="form-control" placeholder="Password" required>
-        </p>
-        <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
-        <a class="btn btn-light btn-block bg-white" href="/oauth2/authorization/google-idp" role="link" style="text-transform: none;">
-            <img width="20" style="margin-right: 5px;" alt="Sign in with Google" src="https://upload.wikimedia.org/wikipedia/commons/thumb/5/53/Google_%22G%22_Logo.svg/512px-Google_%22G%22_Logo.svg.png" />
-            Sign in with Google
-        </a>
-        <a class="btn btn-light btn-block bg-white" href="/oauth2/authorization/github-idp" role="link" style="text-transform: none;">
-            <img width="24" style="margin-right: 5px;" alt="Sign in with GitHub" src="https://github.githubassets.com/images/modules/logos_page/GitHub-Mark.png" />
-            Sign in with Github
-        </a>
+        <h1 class="h3 mb-3 fw-normal">Please sign in</h1>
+        <div class="form-floating">
+            <input type="text" id="username" name="username" class="form-control" required autofocus>
+            <label for="username">Username</label>
+        </div>
+        <div class="form-floating">
+            <input type="password" id="password" name="password" class="form-control" required>
+            <label for="password">Password</label>
+        </div>
+        <div>
+            <button class="w-100 btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
+            <a class="w-100 btn btn-light btn-block bg-white" href="/oauth2/authorization/google-idp" role="link" style="margin-top: 10px">
+                <img src="/assets/img/google.png" th:src="@{/assets/img/google.png}" width="20" style="margin-right: 5px;" alt="Sign in with Google">
+                Sign in with Google
+            </a>
+            <a class="w-100 btn btn-light btn-block bg-white" href="/oauth2/authorization/github-idp" role="link" style="margin-top: 10px">
+                <img src="/assets/img/github.png" th:src="@{/assets/img/github.png}" width="24" style="margin-right: 5px;" alt="Sign in with Github">
+                Sign in with Github
+            </a>
+        </div>
     </form>
 </div>
 </body>
-</html>
+</html>