Commit 2a5ba8e8ad78b302f2174c444443badb22044dad

Authored by Igor Kulikov
1 parent eec4f5a4

Add Apple OAuth2 provider.

Showing 19 changed files with 158 additions and 21 deletions
  1 +{
  2 + "providerId": "Apple",
  3 + "additionalInfo": null,
  4 + "accessTokenUri": "https://appleid.apple.com/auth/token",
  5 + "authorizationUri": "https://appleid.apple.com/auth/authorize?response_mode=form_post",
  6 + "scope": ["email","openid","name"],
  7 + "jwkSetUri": "https://appleid.apple.com/auth/keys",
  8 + "userInfoUri": null,
  9 + "clientAuthenticationMethod": "POST",
  10 + "userNameAttributeName": "email",
  11 + "mapperConfig": {
  12 + "type": "APPLE",
  13 + "basic": {
  14 + "emailAttributeKey": "email",
  15 + "firstNameAttributeKey": "firstName",
  16 + "lastNameAttributeKey": "lastName",
  17 + "tenantNameStrategy": "DOMAIN"
  18 + }
  19 + },
  20 + "comment": null,
  21 + "loginButtonIcon": "apple-logo",
  22 + "loginButtonLabel": "Apple",
  23 + "helpLink": "https://developer.apple.com/sign-in-with-apple/get-started/"
  24 +}
@@ -92,7 +92,7 @@ CREATE TABLE IF NOT EXISTS oauth2_registration ( @@ -92,7 +92,7 @@ CREATE TABLE IF NOT EXISTS oauth2_registration (
92 created_time bigint NOT NULL, 92 created_time bigint NOT NULL,
93 additional_info varchar, 93 additional_info varchar,
94 client_id varchar(255), 94 client_id varchar(255),
95 - client_secret varchar(255), 95 + client_secret varchar(2048),
96 authorization_uri varchar(255), 96 authorization_uri varchar(255),
97 token_uri varchar(255), 97 token_uri varchar(255),
98 scope varchar(255), 98 scope varchar(255),
  1 +/**
  2 + * Copyright © 2016-2021 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 com.fasterxml.jackson.databind.JsonNode;
  19 +import lombok.extern.slf4j.Slf4j;
  20 +import org.springframework.security.oauth2.client.authentication.OAuth2AuthenticationToken;
  21 +import org.springframework.stereotype.Service;
  22 +import org.springframework.util.LinkedMultiValueMap;
  23 +import org.springframework.util.MultiValueMap;
  24 +import org.springframework.util.StringUtils;
  25 +import org.thingsboard.common.util.JacksonUtil;
  26 +import org.thingsboard.server.common.data.oauth2.OAuth2MapperConfig;
  27 +import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
  28 +import org.thingsboard.server.dao.oauth2.OAuth2User;
  29 +import org.thingsboard.server.service.security.model.SecurityUser;
  30 +
  31 +import javax.servlet.http.HttpServletRequest;
  32 +import java.util.HashMap;
  33 +import java.util.Map;
  34 +
  35 +@Service(value = "appleOAuth2ClientMapper")
  36 +@Slf4j
  37 +public class AppleOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper {
  38 +
  39 + private static final String USER = "user";
  40 + private static final String NAME = "name";
  41 + private static final String FIRST_NAME = "firstName";
  42 + private static final String LAST_NAME = "lastName";
  43 + private static final String EMAIL = "email";
  44 +
  45 + @Override
  46 + public SecurityUser getOrCreateUserByClientPrincipal(HttpServletRequest request, OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) {
  47 + OAuth2MapperConfig config = registration.getMapperConfig();
  48 + Map<String, Object> attributes = updateAttributesFromRequestParams(request, token.getPrincipal().getAttributes());
  49 + String email = BasicMapperUtils.getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey());
  50 + OAuth2User oauth2User = BasicMapperUtils.getOAuth2User(email, attributes, config);
  51 +
  52 + return getOrCreateSecurityUserFromOAuth2User(oauth2User, registration);
  53 + }
  54 +
  55 + private static Map<String, Object> updateAttributesFromRequestParams(HttpServletRequest request, Map<String, Object> attributes) {
  56 + Map<String, Object> updated = attributes;
  57 + MultiValueMap<String, String> params = toMultiMap(request.getParameterMap());
  58 + String userValue = params.getFirst(USER);
  59 + if (StringUtils.hasText(userValue)) {
  60 + JsonNode user = null;
  61 + try {
  62 + user = JacksonUtil.toJsonNode(userValue);
  63 + } catch (Exception e) {}
  64 + if (user != null) {
  65 + updated = new HashMap<>(attributes);
  66 + if (user.has(NAME)) {
  67 + JsonNode name = user.get(NAME);
  68 + if (name.isObject()) {
  69 + JsonNode firstName = name.get(FIRST_NAME);
  70 + if (firstName != null && firstName.isTextual()) {
  71 + updated.put(FIRST_NAME, firstName.asText());
  72 + }
  73 + JsonNode lastName = name.get(LAST_NAME);
  74 + if (lastName != null && lastName.isTextual()) {
  75 + updated.put(LAST_NAME, lastName.asText());
  76 + }
  77 + }
  78 + }
  79 + if (user.has(EMAIL)) {
  80 + JsonNode email = user.get(EMAIL);
  81 + if (email != null && email.isTextual()) {
  82 + updated.put(EMAIL, email.asText());
  83 + }
  84 + }
  85 + }
  86 + }
  87 + return updated;
  88 + }
  89 +
  90 + private static MultiValueMap<String, String> toMultiMap(Map<String, String[]> map) {
  91 + MultiValueMap<String, String> params = new LinkedMultiValueMap<>(map.size());
  92 + map.forEach((key, values) -> {
  93 + if (values.length > 0) {
  94 + for (String value : values) {
  95 + params.add(key, value);
  96 + }
  97 + }
  98 + });
  99 + return params;
  100 + }
  101 +}
@@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.oauth2.OAuth2Registration; @@ -23,6 +23,7 @@ import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
23 import org.thingsboard.server.dao.oauth2.OAuth2User; 23 import org.thingsboard.server.dao.oauth2.OAuth2User;
24 import org.thingsboard.server.service.security.model.SecurityUser; 24 import org.thingsboard.server.service.security.model.SecurityUser;
25 25
  26 +import javax.servlet.http.HttpServletRequest;
26 import java.util.Map; 27 import java.util.Map;
27 28
28 @Service(value = "basicOAuth2ClientMapper") 29 @Service(value = "basicOAuth2ClientMapper")
@@ -30,7 +31,7 @@ import java.util.Map; @@ -30,7 +31,7 @@ import java.util.Map;
30 public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper { 31 public class BasicOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper {
31 32
32 @Override 33 @Override
33 - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) { 34 + public SecurityUser getOrCreateUserByClientPrincipal(HttpServletRequest request, OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) {
34 OAuth2MapperConfig config = registration.getMapperConfig(); 35 OAuth2MapperConfig config = registration.getMapperConfig();
35 Map<String, Object> attributes = token.getPrincipal().getAttributes(); 36 Map<String, Object> attributes = token.getPrincipal().getAttributes();
36 String email = BasicMapperUtils.getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey()); 37 String email = BasicMapperUtils.getStringAttributeByKey(attributes, config.getBasic().getEmailAttributeKey());
@@ -29,6 +29,8 @@ import org.thingsboard.server.common.data.oauth2.OAuth2Registration; @@ -29,6 +29,8 @@ import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
29 import org.thingsboard.server.dao.oauth2.OAuth2User; 29 import org.thingsboard.server.dao.oauth2.OAuth2User;
30 import org.thingsboard.server.service.security.model.SecurityUser; 30 import org.thingsboard.server.service.security.model.SecurityUser;
31 31
  32 +import javax.servlet.http.HttpServletRequest;
  33 +
32 @Service(value = "customOAuth2ClientMapper") 34 @Service(value = "customOAuth2ClientMapper")
33 @Slf4j 35 @Slf4j
34 public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper { 36 public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper implements OAuth2ClientMapper {
@@ -39,7 +41,7 @@ public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme @@ -39,7 +41,7 @@ public class CustomOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme
39 private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder(); 41 private RestTemplateBuilder restTemplateBuilder = new RestTemplateBuilder();
40 42
41 @Override 43 @Override
42 - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) { 44 + public SecurityUser getOrCreateUserByClientPrincipal(HttpServletRequest request, OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) {
43 OAuth2MapperConfig config = registration.getMapperConfig(); 45 OAuth2MapperConfig config = registration.getMapperConfig();
44 OAuth2User oauth2User = getOAuth2User(token, providerAccessToken, config.getCustom()); 46 OAuth2User oauth2User = getOAuth2User(token, providerAccessToken, config.getCustom());
45 return getOrCreateSecurityUserFromOAuth2User(oauth2User, registration); 47 return getOrCreateSecurityUserFromOAuth2User(oauth2User, registration);
@@ -29,6 +29,7 @@ import org.thingsboard.server.dao.oauth2.OAuth2Configuration; @@ -29,6 +29,7 @@ import org.thingsboard.server.dao.oauth2.OAuth2Configuration;
29 import org.thingsboard.server.dao.oauth2.OAuth2User; 29 import org.thingsboard.server.dao.oauth2.OAuth2User;
30 import org.thingsboard.server.service.security.model.SecurityUser; 30 import org.thingsboard.server.service.security.model.SecurityUser;
31 31
  32 +import javax.servlet.http.HttpServletRequest;
32 import java.util.ArrayList; 33 import java.util.ArrayList;
33 import java.util.Map; 34 import java.util.Map;
34 import java.util.Optional; 35 import java.util.Optional;
@@ -46,7 +47,7 @@ public class GithubOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme @@ -46,7 +47,7 @@ public class GithubOAuth2ClientMapper extends AbstractOAuth2ClientMapper impleme
46 private OAuth2Configuration oAuth2Configuration; 47 private OAuth2Configuration oAuth2Configuration;
47 48
48 @Override 49 @Override
49 - public SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) { 50 + public SecurityUser getOrCreateUserByClientPrincipal(HttpServletRequest request, OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration) {
50 OAuth2MapperConfig config = registration.getMapperConfig(); 51 OAuth2MapperConfig config = registration.getMapperConfig();
51 Map<String, String> githubMapperConfig = oAuth2Configuration.getGithubMapper(); 52 Map<String, String> githubMapperConfig = oAuth2Configuration.getGithubMapper();
52 String email = getEmail(githubMapperConfig.get(EMAIL_URL_KEY), providerAccessToken); 53 String email = getEmail(githubMapperConfig.get(EMAIL_URL_KEY), providerAccessToken);
@@ -20,6 +20,8 @@ import org.thingsboard.server.common.data.oauth2.OAuth2Registration; @@ -20,6 +20,8 @@ import org.thingsboard.server.common.data.oauth2.OAuth2Registration;
20 import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo; 20 import org.thingsboard.server.common.data.oauth2.deprecated.OAuth2ClientRegistrationInfo;
21 import org.thingsboard.server.service.security.model.SecurityUser; 21 import org.thingsboard.server.service.security.model.SecurityUser;
22 22
  23 +import javax.servlet.http.HttpServletRequest;
  24 +
23 public interface OAuth2ClientMapper { 25 public interface OAuth2ClientMapper {
24 - SecurityUser getOrCreateUserByClientPrincipal(OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration); 26 + SecurityUser getOrCreateUserByClientPrincipal(HttpServletRequest request, OAuth2AuthenticationToken token, String providerAccessToken, OAuth2Registration registration);
25 } 27 }
@@ -37,6 +37,10 @@ public class OAuth2ClientMapperProvider { @@ -37,6 +37,10 @@ public class OAuth2ClientMapperProvider {
37 @Qualifier("githubOAuth2ClientMapper") 37 @Qualifier("githubOAuth2ClientMapper")
38 private OAuth2ClientMapper githubOAuth2ClientMapper; 38 private OAuth2ClientMapper githubOAuth2ClientMapper;
39 39
  40 + @Autowired
  41 + @Qualifier("appleOAuth2ClientMapper")
  42 + private OAuth2ClientMapper appleOAuth2ClientMapper;
  43 +
40 public OAuth2ClientMapper getOAuth2ClientMapperByType(MapperType oauth2MapperType) { 44 public OAuth2ClientMapper getOAuth2ClientMapperByType(MapperType oauth2MapperType) {
41 switch (oauth2MapperType) { 45 switch (oauth2MapperType) {
42 case CUSTOM: 46 case CUSTOM:
@@ -45,6 +49,8 @@ public class OAuth2ClientMapperProvider { @@ -45,6 +49,8 @@ public class OAuth2ClientMapperProvider {
45 return basicOAuth2ClientMapper; 49 return basicOAuth2ClientMapper;
46 case GITHUB: 50 case GITHUB:
47 return githubOAuth2ClientMapper; 51 return githubOAuth2ClientMapper;
  52 + case APPLE:
  53 + return appleOAuth2ClientMapper;
48 default: 54 default:
49 throw new RuntimeException("OAuth2ClientRegistrationMapper with type " + oauth2MapperType + " is not supported!"); 55 throw new RuntimeException("OAuth2ClientRegistrationMapper with type " + oauth2MapperType + " is not supported!");
50 } 56 }
@@ -36,7 +36,6 @@ import java.net.URLEncoder; @@ -36,7 +36,6 @@ import java.net.URLEncoder;
36 import java.nio.charset.StandardCharsets; 36 import java.nio.charset.StandardCharsets;
37 37
38 @Component(value = "oauth2AuthenticationFailureHandler") 38 @Component(value = "oauth2AuthenticationFailureHandler")
39 -@ConditionalOnProperty(prefix = "security.oauth2", value = "enabled", havingValue = "true")  
40 public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler { 39 public class Oauth2AuthenticationFailureHandler extends SimpleUrlAuthenticationFailureHandler {
41 40
42 private final HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository; 41 private final HttpCookieOAuth2AuthorizationRequestRepository httpCookieOAuth2AuthorizationRequestRepository;
@@ -90,7 +90,7 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS @@ -90,7 +90,7 @@ public class Oauth2AuthenticationSuccessHandler extends SimpleUrlAuthenticationS
90 token.getAuthorizedClientRegistrationId(), 90 token.getAuthorizedClientRegistrationId(),
91 token.getPrincipal().getName()); 91 token.getPrincipal().getName());
92 OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(registration.getMapperConfig().getType()); 92 OAuth2ClientMapper mapper = oauth2ClientMapperProvider.getOAuth2ClientMapperByType(registration.getMapperConfig().getType());
93 - SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(token, oAuth2AuthorizedClient.getAccessToken().getTokenValue(), 93 + SecurityUser securityUser = mapper.getOrCreateUserByClientPrincipal(request, token, oAuth2AuthorizedClient.getAccessToken().getTokenValue(),
94 registration); 94 registration);
95 95
96 JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser); 96 JwtToken accessToken = tokenFactory.createAccessJwtToken(securityUser);
@@ -16,5 +16,5 @@ @@ -16,5 +16,5 @@
16 package org.thingsboard.server.common.data.oauth2; 16 package org.thingsboard.server.common.data.oauth2;
17 17
18 public enum MapperType { 18 public enum MapperType {
19 - BASIC, CUSTOM, GITHUB; 19 + BASIC, CUSTOM, GITHUB, APPLE;
20 } 20 }
@@ -178,7 +178,7 @@ public class OAuth2RegistrationEntity extends BaseSqlEntity<OAuth2Registration> @@ -178,7 +178,7 @@ public class OAuth2RegistrationEntity extends BaseSqlEntity<OAuth2Registration>
178 .activateUser(activateUser) 178 .activateUser(activateUser)
179 .type(type) 179 .type(type)
180 .basic( 180 .basic(
181 - (type == MapperType.BASIC || type == MapperType.GITHUB) ? 181 + (type == MapperType.BASIC || type == MapperType.GITHUB || type == MapperType.APPLE) ?
182 OAuth2BasicMapperConfig.builder() 182 OAuth2BasicMapperConfig.builder()
183 .emailAttributeKey(emailAttributeKey) 183 .emailAttributeKey(emailAttributeKey)
184 .firstNameAttributeKey(firstNameAttributeKey) 184 .firstNameAttributeKey(firstNameAttributeKey)
@@ -377,9 +377,6 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se @@ -377,9 +377,6 @@ public class OAuth2ServiceImpl extends AbstractEntityService implements OAuth2Se
377 if (StringUtils.isEmpty(clientRegistration.getScope())) { 377 if (StringUtils.isEmpty(clientRegistration.getScope())) {
378 throw new DataValidationException("Scope should be specified!"); 378 throw new DataValidationException("Scope should be specified!");
379 } 379 }
380 - if (StringUtils.isEmpty(clientRegistration.getUserInfoUri())) {  
381 - throw new DataValidationException("User info uri should be specified!");  
382 - }  
383 if (StringUtils.isEmpty(clientRegistration.getUserNameAttributeName())) { 380 if (StringUtils.isEmpty(clientRegistration.getUserNameAttributeName())) {
384 throw new DataValidationException("User name attribute name should be specified!"); 381 throw new DataValidationException("User name attribute name should be specified!");
385 } 382 }
@@ -387,7 +387,7 @@ CREATE TABLE IF NOT EXISTS oauth2_registration ( @@ -387,7 +387,7 @@ CREATE TABLE IF NOT EXISTS oauth2_registration (
387 created_time bigint NOT NULL, 387 created_time bigint NOT NULL,
388 additional_info varchar, 388 additional_info varchar,
389 client_id varchar(255), 389 client_id varchar(255),
390 - client_secret varchar(255), 390 + client_secret varchar(2048),
391 authorization_uri varchar(255), 391 authorization_uri varchar(255),
392 token_uri varchar(255), 392 token_uri varchar(255),
393 scope varchar(255), 393 scope varchar(255),
@@ -424,7 +424,7 @@ CREATE TABLE IF NOT EXISTS oauth2_registration ( @@ -424,7 +424,7 @@ CREATE TABLE IF NOT EXISTS oauth2_registration (
424 created_time bigint NOT NULL, 424 created_time bigint NOT NULL,
425 additional_info varchar, 425 additional_info varchar,
426 client_id varchar(255), 426 client_id varchar(255),
427 - client_secret varchar(255), 427 + client_secret varchar(2048),
428 authorization_uri varchar(255), 428 authorization_uri varchar(255),
429 token_uri varchar(255), 429 token_uri varchar(255),
430 scope varchar(255), 430 scope varchar(255),
@@ -89,6 +89,13 @@ export class AppComponent implements OnInit { @@ -89,6 +89,13 @@ export class AppComponent implements OnInit {
89 ) 89 )
90 ); 90 );
91 91
  92 + this.matIconRegistry.addSvgIconLiteral(
  93 + 'apple-logo',
  94 + this.domSanitizer.bypassSecurityTrustHtml(
  95 + '<svg viewBox="0 0 256 315"><path d="M213.803394,167.030943 C214.2452,214.609646 255.542482,230.442639 256,230.644727 C255.650812,231.761357 249.401383,253.208293 234.24263,275.361446 C221.138555,294.513969 207.538253,313.596333 186.113759,313.991545 C165.062051,314.379442 158.292752,301.507828 134.22469,301.507828 C110.163898,301.507828 102.642899,313.596301 82.7151126,314.379442 C62.0350407,315.16201 46.2873831,293.668525 33.0744079,274.586162 C6.07529317,235.552544 -14.5576169,164.286328 13.147166,116.18047 C26.9103111,92.2909053 51.5060917,77.1630356 78.2026125,76.7751096 C98.5099145,76.3877456 117.677594,90.4371851 130.091705,90.4371851 C142.497945,90.4371851 165.790755,73.5415029 190.277627,76.0228474 C200.528668,76.4495055 229.303509,80.1636878 247.780625,107.209389 C246.291825,108.132333 213.44635,127.253405 213.803394,167.030988 M174.239142,50.1987033 C185.218331,36.9088319 192.607958,18.4081019 190.591988,0 C174.766312,0.636050225 155.629514,10.5457909 144.278109,23.8283506 C134.10507,35.5906758 125.195775,54.4170275 127.599657,72.4607932 C145.239231,73.8255433 163.259413,63.4970262 174.239142,50.1987249" fill="#000000"></path></svg>'
  96 + )
  97 + );
  98 +
92 this.storageService.testLocalStorage(); 99 this.storageService.testLocalStorage();
93 100
94 this.setupTranslate(); 101 this.setupTranslate();
@@ -321,16 +321,13 @@ @@ -321,16 +321,13 @@
321 321
322 <mat-form-field fxFlex class="mat-block"> 322 <mat-form-field fxFlex class="mat-block">
323 <mat-label translate>admin.oauth2.user-info-uri</mat-label> 323 <mat-label translate>admin.oauth2.user-info-uri</mat-label>
324 - <input matInput formControlName="userInfoUri" required> 324 + <input matInput formControlName="userInfoUri">
325 <button mat-icon-button matSuffix 325 <button mat-icon-button matSuffix
326 type="button" 326 type="button"
327 (click)="toggleEditMode(registration, 'userInfoUri')" 327 (click)="toggleEditMode(registration, 'userInfoUri')"
328 *ngIf="!isCustomProvider(registration)"> 328 *ngIf="!isCustomProvider(registration)">
329 <mat-icon class="material-icons">create</mat-icon> 329 <mat-icon class="material-icons">create</mat-icon>
330 </button> 330 </button>
331 - <mat-error *ngIf="registration.get('userInfoUri').hasError('required')">  
332 - {{ 'admin.oauth2.user-info-uri-required' | translate }}  
333 - </mat-error>  
334 <mat-error *ngIf="registration.get('userInfoUri').hasError('pattern')"> 331 <mat-error *ngIf="registration.get('userInfoUri').hasError('pattern')">
335 {{ 'admin.oauth2.uri-pattern-error' | translate }} 332 {{ 'admin.oauth2.uri-pattern-error' | translate }}
336 </mat-error> 333 </mat-error>
@@ -311,8 +311,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha @@ -311,8 +311,7 @@ export class OAuth2SettingsComponent extends PageComponent implements OnInit, Ha
311 scope: this.fb.array(registration?.scope ? registration.scope : [], OAuth2SettingsComponent.validateScope), 311 scope: this.fb.array(registration?.scope ? registration.scope : [], OAuth2SettingsComponent.validateScope),
312 jwkSetUri: [registration?.jwkSetUri ? registration.jwkSetUri : '', Validators.pattern(this.URL_REGEXP)], 312 jwkSetUri: [registration?.jwkSetUri ? registration.jwkSetUri : '', Validators.pattern(this.URL_REGEXP)],
313 userInfoUri: [registration?.userInfoUri ? registration.userInfoUri : '', 313 userInfoUri: [registration?.userInfoUri ? registration.userInfoUri : '',
314 - [Validators.required,  
315 - Validators.pattern(this.URL_REGEXP)]], 314 + [Validators.pattern(this.URL_REGEXP)]],
316 clientAuthenticationMethod: [ 315 clientAuthenticationMethod: [
317 registration?.clientAuthenticationMethod ? registration.clientAuthenticationMethod : ClientAuthenticationMethod.POST, 316 registration?.clientAuthenticationMethod ? registration.clientAuthenticationMethod : ClientAuthenticationMethod.POST,
318 Validators.required], 317 Validators.required],
@@ -54,7 +54,8 @@ export const domainSchemaTranslations = new Map<DomainSchema, string>( @@ -54,7 +54,8 @@ export const domainSchemaTranslations = new Map<DomainSchema, string>(
54 export enum MapperConfigType{ 54 export enum MapperConfigType{
55 BASIC = 'BASIC', 55 BASIC = 'BASIC',
56 CUSTOM = 'CUSTOM', 56 CUSTOM = 'CUSTOM',
57 - GITHUB = 'GITHUB' 57 + GITHUB = 'GITHUB',
  58 + APPLE = 'APPLE'
58 } 59 }
59 60
60 export enum TenantNameStrategy{ 61 export enum TenantNameStrategy{