diff --git a/oauth2/jwt-jose/src/main/java/org/springframework/security/jwt/JwtClaimAccessor.java b/oauth2/jwt-jose/src/main/java/org/springframework/security/jwt/JwtClaimAccessor.java index 98333335d4..7ec7ed528f 100644 --- a/oauth2/jwt-jose/src/main/java/org/springframework/security/jwt/JwtClaimAccessor.java +++ b/oauth2/jwt-jose/src/main/java/org/springframework/security/jwt/JwtClaimAccessor.java @@ -19,6 +19,7 @@ import org.springframework.security.oauth2.core.ClaimAccessor; import java.net.URL; import java.time.Instant; +import java.util.List; /** * A {@link ClaimAccessor} for the "Registered Claim Names" @@ -41,9 +42,8 @@ public interface JwtClaimAccessor extends ClaimAccessor { return this.getClaimAsString(JwtClaim.SUB); } - default String[] getAudience() { - // TODO Impl JwtClaim.AUD - return null; + default List getAudience() { + return this.getClaimAsStringList(JwtClaim.AUD); } default Instant getExpiresAt() { diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/ClaimAccessor.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/ClaimAccessor.java index ea67ec4739..3038aa2232 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/ClaimAccessor.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/core/ClaimAccessor.java @@ -20,6 +20,9 @@ import org.springframework.util.Assert; import java.net.MalformedURLException; import java.net.URL; import java.time.Instant; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; import java.util.Map; /** @@ -66,4 +69,22 @@ public interface ClaimAccessor { throw new IllegalArgumentException("Unable to convert claim '" + claim + "' to URL: " + ex.getMessage(), ex); } } + + default Map getClaimAsMap(String claim) { + if (!this.containsClaim(claim) || !Map.class.isAssignableFrom(this.getClaims().get(claim).getClass())) { + return null; + } + Map claimFields = new HashMap<>(); + ((Map)this.getClaims().get(claim)).forEach((k, v) -> claimFields.put(k.toString(), v)); + return claimFields; + } + + default List getClaimAsStringList(String claim) { + if (!this.containsClaim(claim) || !List.class.isAssignableFrom(this.getClaims().get(claim).getClass())) { + return null; + } + List claimValues = new ArrayList<>(); + ((List)this.getClaims().get(claim)).forEach(e -> claimValues.add(e.toString())); + return claimValues; + } } diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/oidc/core/Address.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/oidc/core/Address.java index 0470fc5ad7..445926b4bd 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/oidc/core/Address.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/oidc/core/Address.java @@ -15,6 +15,8 @@ */ package org.springframework.security.oauth2.oidc.core; +import java.util.Map; + /** * The Address Claim represents a physical mailing address defined by the OpenID Connect Core 1.0 specification * that can be returned either in the UserInfo Response or the ID Token. @@ -39,4 +41,94 @@ public interface Address { String getCountry(); + class Builder implements Address { + private static final String FORMATTED_FIELD_NAME = "formatted"; + private static final String STREET_ADDRESS_FIELD_NAME = "street_address"; + private static final String LOCALITY_FIELD_NAME = "locality"; + private static final String REGION_FIELD_NAME = "region"; + private static final String POSTAL_CODE_FIELD_NAME = "postal_code"; + private static final String COUNTRY_FIELD_NAME = "country"; + private String formatted; + private String streetAddress; + private String locality; + private String region; + private String postalCode; + private String country; + + public Builder() { + } + + public Builder(Map addressFields) { + this.formatted((String)addressFields.get(FORMATTED_FIELD_NAME)); + this.streetAddress((String)addressFields.get(STREET_ADDRESS_FIELD_NAME)); + this.locality((String)addressFields.get(LOCALITY_FIELD_NAME)); + this.region((String)addressFields.get(REGION_FIELD_NAME)); + this.postalCode((String)addressFields.get(POSTAL_CODE_FIELD_NAME)); + this.country((String)addressFields.get(COUNTRY_FIELD_NAME)); + } + + public Builder formatted(String formatted) { + this.formatted = formatted; + return this; + } + + public Builder streetAddress(String streetAddress) { + this.streetAddress = streetAddress; + return this; + } + + public Builder locality(String locality) { + this.locality = locality; + return this; + } + + public Builder region(String region) { + this.region = region; + return this; + } + + public Builder postalCode(String postalCode) { + this.postalCode = postalCode; + return this; + } + + public Builder country(String country) { + this.country = country; + return this; + } + + public Address build() { + return this; + } + + @Override + public String getFormatted() { + return this.formatted; + } + + @Override + public String getStreetAddress() { + return this.streetAddress; + } + + @Override + public String getLocality() { + return this.locality; + } + + @Override + public String getRegion() { + return this.region; + } + + @Override + public String getPostalCode() { + return this.postalCode; + } + + @Override + public String getCountry() { + return this.country; + } + } } diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/oidc/core/IdTokenClaimAccessor.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/oidc/core/IdTokenClaimAccessor.java index 65e56597a3..c58b1584cc 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/oidc/core/IdTokenClaimAccessor.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/oidc/core/IdTokenClaimAccessor.java @@ -19,6 +19,7 @@ import org.springframework.security.oauth2.core.ClaimAccessor; import java.net.URL; import java.time.Instant; +import java.util.List; /** * A {@link ClaimAccessor} for the "Claims" that can be returned in the ID Token @@ -44,9 +45,8 @@ public interface IdTokenClaimAccessor extends StandardClaimAccessor { return this.getClaimAsString(IdTokenClaim.SUB); } - default String[] getAudience() { - // TODO Impl IdTokenClaim.AUD - return null; + default List getAudience() { + return this.getClaimAsStringList(IdTokenClaim.AUD); } default Instant getExpiresAt() { @@ -69,9 +69,8 @@ public interface IdTokenClaimAccessor extends StandardClaimAccessor { return this.getClaimAsString(IdTokenClaim.ACR); } - default String[] getAuthenticationMethods() { - // TODO Impl IdTokenClaim.AMR - return null; + default List getAuthenticationMethods() { + return this.getClaimAsStringList(IdTokenClaim.AMR); } default String getAuthorizedParty() { diff --git a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/oidc/core/StandardClaimAccessor.java b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/oidc/core/StandardClaimAccessor.java index b47c06f7c7..2b00fa9ec2 100644 --- a/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/oidc/core/StandardClaimAccessor.java +++ b/oauth2/oauth2-core/src/main/java/org/springframework/security/oauth2/oidc/core/StandardClaimAccessor.java @@ -18,6 +18,7 @@ package org.springframework.security.oauth2.oidc.core; import org.springframework.security.oauth2.core.ClaimAccessor; import java.time.Instant; +import java.util.Map; /** * A {@link ClaimAccessor} for the "Standard Claims" that can be returned @@ -107,12 +108,13 @@ public interface StandardClaimAccessor extends ClaimAccessor { } default Address getAddress() { - // TODO Impl StandardClaim.ADDRESS - return null; + Map addressFields = this.getClaimAsMap(StandardClaim.ADDRESS); + return (addressFields != null ? + new Address.Builder(addressFields).build() : + new Address.Builder().build()); } default Instant getUpdatedAt() { return this.getClaimAsInstant(StandardClaim.UPDATED_AT); } - } diff --git a/samples/boot/oauth2login/src/main/resources/META-INF/oauth2-clients-defaults.yml b/samples/boot/oauth2login/src/main/resources/META-INF/oauth2-clients-defaults.yml index 649cda1272..369ed40808 100644 --- a/samples/boot/oauth2login/src/main/resources/META-INF/oauth2-clients-defaults.yml +++ b/samples/boot/oauth2login/src/main/resources/META-INF/oauth2-clients-defaults.yml @@ -5,7 +5,7 @@ security: client-authentication-method: basic authorized-grant-type: authorization_code redirect-uri: "{scheme}://{serverName}:{serverPort}{baseAuthorizeUri}/{clientAlias}" - scopes: openid, email, profile + scopes: openid, profile, email, address, phone authorization-uri: "https://accounts.google.com/o/oauth2/auth" token-uri: "https://accounts.google.com/o/oauth2/token" user-info-uri: "https://www.googleapis.com/oauth2/v3/userinfo" @@ -38,6 +38,6 @@ security: client-authentication-method: basic authorized-grant-type: authorization_code redirect-uri: "{scheme}://{serverName}:{serverPort}{baseAuthorizeUri}/{clientAlias}" - scopes: openid, email, profile + scopes: openid, profile, email, address, phone client-name: Okta client-alias: okta