· 5 years ago · Aug 10, 2020, 10:26 AM
1/*
2 * To change this license header, choose License Headers in Project Properties.
3 * To change this template file, choose Tools | Templates
4 * and open the template in the editor.
5 */
6package iconpln.sinergi.airbizz.mono.web.controller.auth;
7
8import com.fasterxml.jackson.databind.ObjectMapper;
9import iconpln.auth.ldap.pln.LDAPAuthPLNService;
10import iconpln.auth.ldap.pln.model.LDAPAuthResponse;
11import iconpln.common.auth.client.HeaderConstant;
12import iconpln.common.http.filter.exception.FilterException;
13import iconpln.common.json.util.JsonUtil;
14import iconpln.common.model.ModelStatus;
15import iconpln.common.model.http.HttpResponseModel;
16import iconpln.common.util.Check;
17import iconpln.sinergi.airbizz.mono.auth.domain.dto.LoginReq;
18import iconpln.sinergi.airbizz.mono.auth.domain.entity.AirApplicationsToken;
19import iconpln.sinergi.airbizz.mono.auth.domain.entity.AppAuthentication;
20import iconpln.sinergi.airbizz.mono.auth.domain.entity.AuthSsoIam;
21import iconpln.sinergi.airbizz.mono.auth.domain.entity.AuthUserEntity;
22import iconpln.sinergi.airbizz.mono.auth.domain.repository.AppAuthenticationRepository;
23import iconpln.sinergi.airbizz.mono.auth.domain.repository.AuthSsoIamRepository;
24import iconpln.sinergi.airbizz.mono.auth.domain.repository.AuthUserRepository;
25import iconpln.sinergi.airbizz.mono.service.auth.RegisterAccountService;
26import iconpln.sinergi.airbizz.mono.service.auth.VersionService;
27import iconpln.sinergi.airbizz.mono.service.auth.util.ApplicationsTokenUtil;
28import iconpln.sinergi.airbizz.mono.service.auth.util.Constant;
29import iconpln.sinergi.airbizz.mono.service.customer.customer.CustomerService;
30import iconpln.sinergi.airbizz.mono.transaction.model.model.MarketplaceUserRole;
31import iconpln.sinergi.airbizz.mono.web.config.AirBizzContextPath;
32import iconpln.sinergi.pandu.customer.request.AuthRegister;
33import iconpln.sinergi.pandu.customer.shared.CustomerDto;
34import iconpln.sinergi.pandu.customer.shared.CustomerRole;
35import iconpln.sinergi.pandu.reference.response.UnitCompanyDto;
36import io.jsonwebtoken.Jwts;
37import io.jsonwebtoken.SignatureAlgorithm;
38import io.swagger.annotations.Api;
39import io.swagger.annotations.ApiImplicitParam;
40import io.swagger.annotations.ApiImplicitParams;
41import io.swagger.annotations.ApiOperation;
42import lombok.extern.slf4j.Slf4j;
43import org.apache.commons.lang3.StringUtils;
44import org.asynchttpclient.AsyncHttpClient;
45import org.asynchttpclient.Response;
46import org.asynchttpclient.util.HttpConstants;
47import org.mindrot.jbcrypt.BCrypt;
48import org.slf4j.Logger;
49import org.slf4j.LoggerFactory;
50import org.springframework.beans.factory.annotation.Autowired;
51import org.springframework.beans.factory.annotation.Value;
52import org.springframework.http.HttpStatus;
53import org.springframework.http.MediaType;
54import org.springframework.http.ResponseEntity;
55import org.springframework.web.bind.annotation.*;
56
57import javax.servlet.http.HttpServletRequest;
58import javax.servlet.http.HttpServletResponse;
59import java.security.AccessControlException;
60import java.util.*;
61import java.util.concurrent.CompletableFuture;
62
63/**
64 * @author dhamarsu
65 */
66@CrossOrigin
67@RestController
68@RequestMapping(AirBizzContextPath.AUTH_CONTEXT_PATH + "/user")
69@Api(value = "Auth User API", produces = MediaType.APPLICATION_JSON_VALUE, tags = {"Auth"})
70@Slf4j
71public class AuthUserController {
72
73 private final Logger LOG = LoggerFactory.getLogger(AuthUserController.class);
74
75 @Autowired
76 private AuthUserRepository authUserRepository;
77
78 @Autowired
79// private CommonCustomerService customerService;
80 private CustomerService customerService;
81
82 @Autowired
83 private VersionService versionService;
84
85 @Autowired
86 private LDAPAuthPLNService ldapAuthPLNService;
87
88 @Autowired
89 private AppAuthenticationRepository appAuthRepository;
90
91 @Autowired
92 private RegisterAccountService registerAccountService;
93
94 @Autowired
95 private AsyncHttpClient asyncHttpClient;
96
97 @Autowired
98 private ObjectMapper objectMapper;
99 @Autowired
100 private AuthSsoIamRepository authSsoIamRepository;
101
102 @RequestMapping(value = "/disable", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
103 @ApiOperation(value = "API Disable User", response = HttpResponseModel.class)
104 @ApiImplicitParams({
105 @ApiImplicitParam(name = HeaderConstant.APP_ID, required = false, paramType = "header", dataType = "string"),
106 @ApiImplicitParam(name = HeaderConstant.TIME_STAMP, required = false, paramType = "header", dataType = "string"),
107 @ApiImplicitParam(name = HeaderConstant.TOKEN, required = false, paramType = "header", dataType = "string"),
108 })
109 public ResponseEntity disableUser(@RequestParam(required = true) String userid) {
110 AuthUserEntity user = authUserRepository.findById(userid).get();
111 user.setStatus(ModelStatus.DISABLE.name());
112 user.setDateUpdated(new Date());
113 authUserRepository.save(user);
114 user.setPassword("");
115 return ResponseEntity.ok(HttpResponseModel.ok(user, "Success"));
116 }
117
118 @RequestMapping(value = "/enable", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
119 @ApiOperation(value = "API Enable User", response = HttpResponseModel.class)
120 @ApiImplicitParams({
121 @ApiImplicitParam(name = HeaderConstant.APP_ID, required = false, paramType = "header", dataType = "string"),
122 @ApiImplicitParam(name = HeaderConstant.TIME_STAMP, required = false, paramType = "header", dataType = "string"),
123 @ApiImplicitParam(name = HeaderConstant.TOKEN, required = false, paramType = "header", dataType = "string"),
124 })
125 public ResponseEntity enableUser(@RequestParam(required = true) String userid) {
126 AuthUserEntity user = authUserRepository.findById(userid).get();
127 user.setStatus(ModelStatus.ACTIVE.name());
128 user.setDateUpdated(new Date());
129 authUserRepository.save(user);
130 user.setPassword("");
131 return ResponseEntity.ok(HttpResponseModel.ok(user, "Success"));
132 }
133
134 @RequestMapping(value = "/register", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
135 @ApiOperation(value = "API Register User (please use /customer/registration instead)", response = HttpResponseModel.class)
136 @ApiImplicitParam(name = HeaderConstant.APPSOURCE, required = false, paramType = "header", dataType = "string")
137 public ResponseEntity registerUser(HttpServletRequest req, @RequestBody AuthRegister register) {
138 LOG.info("register userid:{} email:{}", register.getUserid(), register.getEmail());
139 String appsource = req.getHeader(HeaderConstant.APPSOURCE);
140 AuthUserEntity user = registerAccountService.registerUser(appsource, register);
141 authUserRepository.save(user);
142 return ResponseEntity.ok(HttpResponseModel.ok(ApplicationsTokenUtil.getToken(user, appsource)));
143 }
144
145 @RequestMapping(value = "/resend/register", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
146 @ApiOperation(value = "API Resend Register User", response = HttpResponseModel.class)
147 @ApiImplicitParam(name = HeaderConstant.APPSOURCE, required = false, paramType = "header", dataType = "string")
148 public ResponseEntity resendVerificationRegisterUser(@RequestParam String email) {
149 HttpResponseModel model = registerAccountService.resendVerificationRegister(email);
150 return ResponseEntity.ok(model);
151 }
152
153 @RequestMapping(value = "/confirmRegister", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
154 @ApiOperation(value = "API Confirm User Registration", response = HttpResponseModel.class)
155 public ResponseEntity confirmRegister(@RequestParam(required = true) String code) {
156 LOG.error("confirm registration " + code);
157 String userId = registerAccountService.confirmRegister(code);
158 return ResponseEntity.ok(HttpResponseModel.ok(userId, "Success."));
159 }
160
161 @RequestMapping(value = "/login", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
162 @ApiOperation(value = "API Login", response = HttpResponseModel.class)
163 @ApiImplicitParam(name = HeaderConstant.APPSOURCE, required = false, paramType = "header", dataType = "string")
164 public ResponseEntity login(HttpServletRequest req, HttpServletResponse resp, @RequestBody LoginReq login) {
165
166 HttpResponseModel model;
167 try {
168 if (!versionService.validateVersion(req)) {
169 model = HttpResponseModel.error(410, "mohon update aplikasi anda");
170 return new ResponseEntity<>(model, HttpStatus.GONE);
171 }
172
173 Check.isTrue((StringUtils.isNotBlank(login.getEmail()) || StringUtils.isNotBlank(login.getUserid()))
174 && StringUtils.isNotBlank(login.getPassword()));
175
176 String userid = StringUtils.isNotBlank(login.getUserid()) ? login.getUserid() : login.getEmail();
177 String password = login.getPassword();
178 String appsource = req.getHeader(HeaderConstant.APPSOURCE);
179
180 if (!appsource.equals(iconpln.sinergi.airbizz.mono.web.config.Constant.APPSOURCE_WEB)
181 && !appsource.equals(iconpln.sinergi.airbizz.mono.web.config.Constant.APPSOURCE_BACKOFFICE)
182 && !appsource.equals(iconpln.sinergi.airbizz.mono.web.config.Constant.APPSOURCE_MOBILE)
183 ) {
184 throw new RuntimeException("AppSource not allowed");
185 }
186
187 AuthUserEntity user = authUserRepository.findOneByUseridOrEmail(userid.toLowerCase(), userid.toLowerCase());
188 Check.nonNull(user);
189 String hashed = user.getPassword();
190
191 AppAuthentication.Method method = getAuthMethod(appsource);
192 if (method == AppAuthentication.Method.LDAP) {
193 LDAPAuthResponse ldapAuthResponse = ldapAuthPLNService.validate(userid.toLowerCase(), login.getPassword());
194 LOG.info("response from LDAP: {} [detail:{}]", ldapAuthResponse.getMessage(), ldapAuthResponse.getMessageDetail());
195 Check.isTrue(ldapAuthResponse.isSuccess(), ldapAuthResponse.getMessage());
196 } else {
197 Check.hasText(user.getPassword());
198 Check.isTrue(user.getStatus().equalsIgnoreCase(ModelStatus.ACTIVE.name()), new AccessControlException(""));
199 Check.isTrue(BCrypt.checkpw(password, hashed));
200 }
201
202 String urlUserId = user.getUserid().replace("\\", "%5c");
203 CustomerDto dto = customerService.findByUseridOrEmailOpt(urlUserId, urlUserId).orElse(null);
204 Check.nonNull(dto, new FilterException(""));
205 checkRole(dto, appsource);
206 UnitCompanyDto uc = dto.getUnitCompany();
207 if (uc != null) {
208 uc.setAddress(Collections.emptyList());
209 if (uc.getCompany() != null) {
210 uc.setCompany(uc.getCompany().tnc("").address(Collections.emptyList()));
211 }
212 }
213
214 List<AirApplicationsToken> airApplicationsTokens = user.getAirAppsTokens() != null ? user.getAirAppsTokens() : new ArrayList<>();
215 String jwtToken = Jwts.builder().setSubject(JsonUtil.encode(dto)).claim("roles", "user").setIssuedAt(new Date())
216 .signWith(SignatureAlgorithm.HS256, Constant.JWT_SECRET_KEY).compact();
217 if (StringUtils.compareIgnoreCase(HeaderConstant.WEB_BACK_OFFICE_HEADER, appsource) == 0
218 || StringUtils.compareIgnoreCase(HeaderConstant.WEB_TRX_APPS_HEADER, appsource) == 0
219 || StringUtils.compareIgnoreCase(HeaderConstant.WEB_EIS_HEADER, appsource) == 0) {
220 Optional<AirApplicationsToken> appTokenOpt = airApplicationsTokens.stream().filter(val -> val.getAirAppSource().equalsIgnoreCase(appsource))
221 .findFirst();
222 if (appTokenOpt.isPresent()) {
223 appTokenOpt.get().setToken(jwtToken);
224 } else {
225 airApplicationsTokens.add(new AirApplicationsToken(appsource, jwtToken, login.getFcmKey()));
226 }
227 } else {
228 Optional<AirApplicationsToken> appTokenOpt = airApplicationsTokens.stream().filter(val -> val.getAirAppSource().equalsIgnoreCase(appsource))
229 .findFirst();
230 if (appTokenOpt.isPresent()) {
231 appTokenOpt.get().setToken(jwtToken);
232 appTokenOpt.get().setFcmKey(login.getFcmKey());
233 } else {
234 airApplicationsTokens.add(new AirApplicationsToken(appsource, jwtToken, login.getFcmKey()));
235 }
236 }
237
238// user.setAirAppsTokens(ApplicationsTokenUtil.update(user.getAirAppsTokens(), appsource, jwtToken));
239 user.setAirAppsTokens(airApplicationsTokens);
240 user.setPassword(hashed);
241 user.setDateUpdated(new Date());
242 authUserRepository.save(user);
243
244 resp.setHeader(HeaderConstant.AUTHORIZATION, jwtToken);
245 model = HttpResponseModel.ok(jwtToken);
246 } catch (AccessControlException e) {
247 model = HttpResponseModel.error(400, "Akun ini sedang dinonaktifkan");
248 } catch (FilterException e) {
249 model = HttpResponseModel.error(400, "Akun ini tidak punya akses terhadap aplikasi");
250 } catch (Exception e) {
251 model = HttpResponseModel.error(400, "Gagal login, pastikan userid/email & password sesuai");
252 }
253 return ResponseEntity.ok(model);
254 }
255
256 @Value("${app.sso.app-secret-key}")
257 private String ssoAppKeySecret;
258 @Value("${app.sso.app-id}")
259 private String ssoAppId;
260 @Value("${app.sso.token-url}")
261 private String ssoTokenUrl;
262 @Value("${app.sso.profile-url}")
263 private String ssoProfileUrl;
264 @Value("${app.sso.redirect-url}")
265 private String ssoRedirectUrl;
266 @Value("${app.sso.login-url}")
267 private String ssoLoginUrl;
268
269 @GetMapping(value = "/login/sso-url", produces = MediaType.APPLICATION_JSON_VALUE)
270 @ApiOperation(value = "API Get Login Sso URL", response = HttpResponseModel.class)
271 @ApiImplicitParam(name = HeaderConstant.APPSOURCE, required = false, paramType = "header", dataType = "string")
272 public ResponseEntity getSsoLoginUrl() {
273 HttpResponseModel<String> model = HttpResponseModel.ok(ssoLoginUrl);
274 return ResponseEntity.ok(model);
275 }
276
277 @Value("${app.sso.logout-url}")
278 private String ssoLogoutUri;
279
280 @GetMapping(value = "/login/sso-logout-url", produces = MediaType.APPLICATION_JSON_VALUE)
281 @ApiOperation(value = "API Get Logout Sso URL", response = HttpResponseModel.class)
282 @ApiImplicitParam(name = HeaderConstant.APPSOURCE, required = false, paramType = "header", dataType = "string")
283 public ResponseEntity getSsoLogoutUrl() {
284 HttpResponseModel<String> model = HttpResponseModel.ok(ssoLogoutUri);
285 return ResponseEntity.ok(model);
286 }
287
288 @RequestMapping(value = "/login/sso", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
289 @ApiOperation(value = "API Login Sso", response = HttpResponseModel.class)
290 @ApiImplicitParam(name = HeaderConstant.APPSOURCE, required = false, paramType = "header", dataType = "string")
291 public ResponseEntity loginSso(HttpServletRequest req, HttpServletResponse resp, @RequestBody String ssoCode) {
292 HttpResponseModel model;
293 try {
294 if (!versionService.validateVersion(req)) {
295 model = HttpResponseModel.error(410, "mohon update aplikasi anda");
296 return new ResponseEntity<>(model, HttpStatus.GONE);
297 }
298 String appsource = req.getHeader(HeaderConstant.APPSOURCE);
299
300 /* get profile from sso*/
301 String authValue = new String(Base64.getEncoder().encode((ssoAppId + ":" + ssoAppKeySecret).getBytes()));
302
303 Map<CharSequence, String> headers = new HashMap<>();
304 headers.put("Content-Type", "application/x-www-form-urlencoded");
305 headers.put("Authorization", "Basic " + authValue);
306
307 CompletableFuture<Response> completableFuture = asyncHttpClient.preparePost(ssoTokenUrl)
308 .setSingleHeaders(headers)
309 .addFormParam("code", ssoCode)
310 .addFormParam("redirect_uri", ssoRedirectUrl)
311 .addFormParam("grant_type", "authorization_code")
312 .execute().toCompletableFuture();
313 Response responseToken = completableFuture.get();
314 Map<String, Object> resultToken = objectMapper.readValue(responseToken.getResponseBody(), Map.class);
315 if (responseToken.getStatusCode() != HttpConstants.ResponseStatusCodes.OK_200) {
316 log.info("SSO Resp: " + responseToken.getResponseBody());
317 return ResponseEntity.ok(HttpResponseModel.error((String) resultToken.get("error_description")));
318 }
319
320 String accessToken = (String) resultToken.get("access_token");
321 String idToken = (String) resultToken.get("id_token");
322 String tokenType = (String) resultToken.get("token_type");
323
324 Response responseProfile = asyncHttpClient.prepareGet(ssoProfileUrl)
325 .addHeader("Authorization", tokenType + " " + accessToken)
326 .execute().get();
327
328 Map<String, Object> resultProfile = objectMapper.readValue(responseProfile.getResponseBody(), Map.class);
329 if (responseProfile.getStatusCode() != HttpConstants.ResponseStatusCodes.OK_200) {
330 return ResponseEntity.ok(HttpResponseModel.error((String) resultProfile.get("error_description")));
331 }
332 String email = (String) resultProfile.get("email");
333 String userid = (String) resultProfile.get("preferred_username");
334 String name = (String) resultProfile.get("name");
335 /* end */
336
337 log.info("=== do find user auth with user id : [{}] ===", userid);
338 AuthUserEntity user = authUserRepository.findById(userid.toLowerCase()).orElse(null);
339 if (user == null) {
340 log.error("=== user dengan user id : [{}] belum terdaftar! ===", userid);
341 model = HttpResponseModel.error(404, "User belum terdaftar", null);
342 return ResponseEntity.ok(model);
343 }
344 if (user.getStatus() != null && user.getStatus().equals("DISABLE")) {
345 throw new RuntimeException("User is disabled.");
346 }
347 String hashed = user.getPassword();
348
349 String urlUserId = user.getUserid().replace("\\", "%5c");
350 CustomerDto dto = customerService.findByUseridOrEmailOpt(urlUserId, urlUserId).orElse(null);
351 Check.nonNull(dto, new FilterException(""));
352 checkRole(dto, appsource);
353 UnitCompanyDto uc = dto.getUnitCompany();
354 if (uc != null) {
355 uc.setAddress(Collections.emptyList());
356 if (uc.getCompany() != null) {
357 uc.setCompany(uc.getCompany().tnc("").address(Collections.emptyList()));
358 }
359 }
360
361 List<AirApplicationsToken> airApplicationsTokens = user.getAirAppsTokens() != null ? user.getAirAppsTokens() : new ArrayList<>();
362 String jwtToken = Jwts.builder().setSubject(JsonUtil.encode(dto)).claim("roles", "user").setIssuedAt(new Date())
363 .signWith(SignatureAlgorithm.HS256, Constant.JWT_SECRET_KEY).compact();
364 if (StringUtils.compareIgnoreCase(HeaderConstant.WEB_BACK_OFFICE_HEADER, appsource) == 0
365 || StringUtils.compareIgnoreCase(HeaderConstant.WEB_TRX_APPS_HEADER, appsource) == 0
366 || StringUtils.compareIgnoreCase(HeaderConstant.WEB_EIS_HEADER, appsource) == 0) {
367 Optional<AirApplicationsToken> appTokenOpt = airApplicationsTokens.stream().filter(val -> val.getAirAppSource().equalsIgnoreCase(appsource))
368 .findFirst();
369 if (appTokenOpt.isPresent()) {
370 appTokenOpt.get().setToken(jwtToken);
371 } else {
372 airApplicationsTokens.add(new AirApplicationsToken(appsource, jwtToken));
373 }
374 } else {
375 Optional<AirApplicationsToken> appTokenOpt = airApplicationsTokens.stream().filter(val -> val.getAirAppSource().equalsIgnoreCase(appsource))
376 .findFirst();
377 if (appTokenOpt.isPresent()) {
378 appTokenOpt.get().setToken(jwtToken);
379 } else {
380 airApplicationsTokens.add(new AirApplicationsToken(appsource, jwtToken));
381 }
382 }
383
384// user.setAirAppsTokens(ApplicationsTokenUtil.update(user.getAirAppsTokens(), appsource, jwtToken));
385 user.setAirAppsTokens(airApplicationsTokens);
386 user.setPassword(hashed);
387 user.setDateUpdated(new Date());
388 authUserRepository.save(user);
389
390 // remove dot key
391 List<String> keyToBeRemoved = new ArrayList<>();
392 for (Map.Entry<String, Object> entry : resultProfile.entrySet()) {
393 if (entry.getKey().contains(".")) {
394 keyToBeRemoved.add(entry.getKey());
395 }
396 }
397 keyToBeRemoved.forEach(val -> {
398 String newKey = val.replace(".", "");
399 resultProfile.put(newKey, resultProfile.getOrDefault(val, null));
400 resultProfile.remove(val);
401 });
402
403 AuthSsoIam authSsoIam = authSsoIamRepository.save(AuthSsoIam.builder()
404 .userId(userid)
405 .profiles(resultProfile)
406 .tokens(resultToken)
407 .build());
408
409 resp.setHeader(HeaderConstant.AUTHORIZATION, jwtToken);
410 model = HttpResponseModel.ok(jwtToken);
411 } catch (AccessControlException e) {
412 e.printStackTrace();
413 model = HttpResponseModel.error(400, "Akun ini sedang dinonaktifkan");
414 } catch (FilterException e) {
415 e.printStackTrace();
416 model = HttpResponseModel.error(400, "Akun ini tidak punya akses terhadap aplikasi");
417 } catch (Exception e) {
418 e.printStackTrace();
419 model = HttpResponseModel.error(400, "Gagal login, pastikan userid/email & password sesuai");
420 }
421 return ResponseEntity.ok(model);
422 }
423
424 private boolean checkRole(CustomerDto dto, String appsource) {
425 if (HeaderConstant.TRX_APPS_HEADER.equalsIgnoreCase(appsource)
426 || HeaderConstant.WEB_TRX_APPS_HEADER.equalsIgnoreCase(appsource)) {
427 Check.isTrue(dto.getRole().contains(CustomerRole.CUSTOMER.name()), new FilterException(""));
428 } else if (HeaderConstant.FIELD_SERVICE_HEADER.equalsIgnoreCase(appsource)) {
429 Check.isTrue(dto.getRole().contains(CustomerRole.DRIVER.name()) ||
430 dto.getRole().contains(CustomerRole.DISPATCHER.name()), new FilterException(""));
431 } else if (HeaderConstant.FIELD_SERVICE_WAREHOUSE_HEADER.equalsIgnoreCase(appsource)) {
432 Check.isTrue(dto.getRole().contains(CustomerRole.WAREHOUSE_ADMIN.name()), new FilterException(""));
433 } else if (appsource.contains("BACK_OFFICE")) {
434 Check.isTrue(dto.getRole().contains(CustomerRole.BACK_OFFICE.name()), new FilterException(""));
435 } else if (HeaderConstant.WEB_EIS_HEADER.equalsIgnoreCase(appsource)) {
436 Check.isTrue(dto.getRole().contains(CustomerRole.EIS.name()), new FilterException(""));
437 }
438 return true;
439 }
440
441 private AppAuthentication.Method getAuthMethod(String appId) {
442 Optional<AppAuthentication> auth = appAuthRepository.findById(appId);
443 return auth.isPresent()
444 ? auth.get().getAuthMethod()
445 : AppAuthentication.Method.DATABASE;
446 }
447
448}
449