Commit d9bfd829260df7e313e92472e83a4f87e95d93b9

Authored by Igor Kulikov
1 parent 9b18c408

Introduce OAuth failure handling

... ... @@ -79,11 +79,18 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
79 79 @Qualifier("oauth2AuthenticationSuccessHandler")
80 80 private AuthenticationSuccessHandler oauth2AuthenticationSuccessHandler;
81 81
  82 + @Autowired(required = false)
  83 + @Qualifier("oauth2AuthenticationFailureHandler")
  84 + private AuthenticationFailureHandler oauth2AuthenticationFailureHandler;
  85 +
82 86 @Autowired
83 87 @Qualifier("defaultAuthenticationSuccessHandler")
84 88 private AuthenticationSuccessHandler successHandler;
85 89
86   - @Autowired private AuthenticationFailureHandler failureHandler;
  90 + @Autowired
  91 + @Qualifier("defaultAuthenticationFailureHandler")
  92 + private AuthenticationFailureHandler failureHandler;
  93 +
87 94 @Autowired private RestAuthenticationProvider restAuthenticationProvider;
88 95 @Autowired private JwtAuthenticationProvider jwtAuthenticationProvider;
89 96 @Autowired private RefreshTokenAuthenticationProvider refreshTokenAuthenticationProvider;
... ... @@ -205,7 +212,7 @@ public class ThingsboardSecurityConfiguration extends WebSecurityConfigurerAdapt
205 212 .loginPage("/oauth2Login")
206 213 .loginProcessingUrl(oauth2Configuration.getLoginProcessingUrl())
207 214 .successHandler(oauth2AuthenticationSuccessHandler)
208   - .failureHandler(failureHandler);
  215 + .failureHandler(oauth2AuthenticationFailureHandler);
209 216 }
210 217 }
211 218
... ...
  1 +/**
  2 + * Copyright © 2016-2020 The Thingsboard Authors
  3 + *
  4 + * Licensed under the Apache License, Version 2.0 (the "License");
  5 + * you may not use this file except in compliance with the License.
  6 + * You may obtain a copy of the License at
  7 + *
  8 + * http://www.apache.org/licenses/LICENSE-2.0
  9 + *
  10 + * Unless required by applicable law or agreed to in writing, software
  11 + * distributed under the License is distributed on an "AS IS" BASIS,
  12 + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  13 + * See the License for the specific language governing permissions and
  14 + * limitations under the License.
  15 + */
  16 +package org.thingsboard.server.service.security.auth.oauth2;
  17 +
  18 +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
  19 +import org.springframework.security.core.AuthenticationException;
  20 +import org.springframework.security.web.authentication.SimpleUrlAuthenticationFailureHandler;
  21 +import org.springframework.stereotype.Component;
  22 +import org.thingsboard.server.utils.MiscUtils;
  23 +
  24 +import javax.servlet.ServletException;
  25 +import javax.servlet.http.HttpServletRequest;
  26 +import javax.servlet.http.HttpServletResponse;
  27 +import java.io.IOException;
  28 +import java.net.URLEncoder;
  29 +import java.nio.charset.StandardCharsets;
  30 +
  31 +@Component(value = "oauth2AuthenticationFailureHandler")
  32 +@ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true")
  33 +public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
  34 +
  35 + @Override
  36 + public void onAuthenticationFailure(HttpServletRequest request,
  37 + HttpServletResponse response, AuthenticationException exception)
  38 + throws IOException, ServletException {
  39 + String baseUrl = MiscUtils.constructBaseUrl(request);
  40 + getRedirectStrategy().sendRedirect(request, response, baseUrl + "/login?loginError=" +
  41 + URLEncoder.encode(exception.getMessage(), StandardCharsets.UTF_8.toString()));
  42 + }
  43 +}
... ...
... ... @@ -26,7 +26,7 @@ import javax.servlet.http.HttpServletRequest;
26 26 import javax.servlet.http.HttpServletResponse;
27 27 import java.io.IOException;
28 28
29   -@Component
  29 +@Component(value = "defaultAuthenticationFailureHandler")
30 30 public class RestAwareAuthenticationFailureHandler implements AuthenticationFailureHandler {
31 31
32 32 private final ThingsboardErrorResponseHandler errorResponseHandler;
... ...
... ... @@ -22,7 +22,7 @@ export default angular.module('thingsboard.api.user', [thingsboardApiLogin,
22 22 .name;
23 23
24 24 /*@ngInject*/
25   -function UserService($http, $q, $rootScope, adminService, dashboardService, timeService, loginService, toast, store, jwtHelper, $translate, $state, $location) {
  25 +function UserService($http, $q, $rootScope, adminService, dashboardService, timeService, loginService, toast, store, jwtHelper, $translate, $state, $location, $mdDialog) {
26 26 var currentUser = null,
27 27 currentUserDetails = null,
28 28 lastPublicDashboardId = null,
... ... @@ -406,6 +406,10 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, time
406 406 }, function fail() {
407 407 deferred.reject();
408 408 });
  409 + } else if (locationSearch.loginError) {
  410 + showLoginErrorDialog(locationSearch.loginError);
  411 + $location.search('loginError', null);
  412 + deferred.reject();
409 413 } else {
410 414 procceedJwtTokenValidate();
411 415 }
... ... @@ -415,6 +419,17 @@ function UserService($http, $q, $rootScope, adminService, dashboardService, time
415 419 return deferred.promise;
416 420 }
417 421
  422 + function showLoginErrorDialog(loginError) {
  423 + $translate(['login.error',
  424 + 'action.close']).then(function (translations) {
  425 + var alert = $mdDialog.alert()
  426 + .title(translations['login.error'])
  427 + .htmlContent(loginError)
  428 + .ok(translations['action.close']);
  429 + $mdDialog.show(alert);
  430 + });
  431 + }
  432 +
418 433 function loadIsUserTokenAccessEnabled() {
419 434 var deferred = $q.defer();
420 435 if (currentUser.authority === 'SYS_ADMIN' || currentUser.authority === 'TENANT_ADMIN') {
... ...
... ... @@ -1334,7 +1334,8 @@
1334 1334 "password-link-sent-message": "Password reset link was successfully sent!",
1335 1335 "email": "Email",
1336 1336 "login-with": "Login with {{name}}",
1337   - "or": "or"
  1337 + "or": "or",
  1338 + "error": "Login error"
1338 1339 },
1339 1340 "position": {
1340 1341 "top": "Top",
... ...