Skip to content

Commit

Permalink
fix: proper azure b2c provider scope
Browse files Browse the repository at this point in the history
In order to get an access token, must provide the clientID as a scope for azure B2C

JIRA: LX-650
risk: low
  • Loading branch information
chrisbonilla95 committed Nov 14, 2024
1 parent fd4e264 commit 751cb3f
Show file tree
Hide file tree
Showing 2 changed files with 41 additions and 18 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -403,7 +403,7 @@ private fun ClientRegistration.Builder.withRedirectUri(oauthIssuerId: String?) =
* @param organization the organization containing OIDC issuer configuration
* @return this builder
*/
private fun ClientRegistration.Builder.buildWithIssuerConfig(
internal fun ClientRegistration.Builder.buildWithIssuerConfig(
organization: Organization,
): ClientRegistration {
if (organization.oauthClientId == null || organization.oauthClientSecret == null) {
Expand All @@ -418,7 +418,7 @@ private fun ClientRegistration.Builder.buildWithIssuerConfig(
.authorizationGrantType(AuthorizationGrantType.AUTHORIZATION_CODE)
.userNameAttributeName("name")
val supportedScopes = withIssuerConfigBuilder.build().resolveSupportedScopes()
return withIssuerConfigBuilder.withScopes(supportedScopes, organization.jitEnabled).build()
return withIssuerConfigBuilder.withScopes(supportedScopes, organization).build()
}

private fun ClientRegistration.resolveSupportedScopes() =
Expand All @@ -428,16 +428,27 @@ private fun ClientRegistration.resolveSupportedScopes() =

private fun ClientRegistration.Builder.withScopes(
supportedScopes: Scope?,
jitEnabled: Boolean?
organization: Organization

): ClientRegistration.Builder {
// in the future, we could check mandatory scopes against the supported ones
val mandatoryScopes = listOf(OIDCScopeValue.OPENID, OIDCScopeValue.PROFILE).map(Scope.Value::getValue)
val userGroupsScope = if (jitEnabled == true) listOf(OIDCScopeValue.EMAIL.value, GD_USER_GROUPS_SCOPE) else listOf()
val userGroupsScope = if (organization.jitEnabled == true) {
listOf(OIDCScopeValue.EMAIL.value, GD_USER_GROUPS_SCOPE)
} else {
listOf()
}
val azureB2CScope = if (organization.oauthIssuerLocation != null &&
organization.oauthIssuerLocation.toUri().isAzureB2C()) {
listOf(organization.oauthClientId)
} else {
listOf()
}
val optionalScopes = supportedScopes
?.filter { scope -> scope in listOf(OIDCScopeValue.OFFLINE_ACCESS) }
?.map(Scope.Value::getValue)
?: listOf()
return scope(mandatoryScopes + optionalScopes + userGroupsScope)
return scope(mandatoryScopes + optionalScopes + userGroupsScope + azureB2CScope)
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -167,25 +167,36 @@ internal class AuthenticationUtilsTest {

@Test
fun `building fromOidcConfiguration should set values from provided metadata`() {
val azureB2CIssuerId = "someAzureB2CIssuerId"
organization = Organization(
id = ORGANIZATION_ID,
oauthClientId = CLIENT_ID,
oauthIssuerLocation = "https://tenant.b2clogin.com/tenant.onmicrosoft.com/policy/v2.0/",
oauthIssuerId = azureB2CIssuerId,
oauthClientSecret = CLIENT_SECRET
)
val clientRegistrationBuilder = fromOidcConfiguration(VALID_AZURE_B2C_OIDC_CONFIG)
.clientId(CLIENT_ID)
.clientSecret(CLIENT_SECRET)

expect { that(clientRegistrationBuilder) }
val issuer: String = VALID_AZURE_B2C_OIDC_CONFIG["issuer"].toString()
val clientRegistration = clientRegistrationBuilder.build()
assertEquals(URI.create(issuer).host, clientRegistration.registrationId)
assertEquals(IdTokenClaimNames.SUB, clientRegistration.providerDetails.userInfoEndpoint.userNameAttributeName)
assertEquals(AuthorizationGrantType.AUTHORIZATION_CODE, clientRegistration.authorizationGrantType)
assertEquals("{baseUrl}/{action}/oauth2/code/{registrationId}", clientRegistration.redirectUri)
assertEquals(
VALID_AZURE_B2C_OIDC_CONFIG["authorization_endpoint"],
clientRegistration.providerDetails.authorizationUri
)
assertEquals(VALID_AZURE_B2C_OIDC_CONFIG, clientRegistration.providerDetails.configurationMetadata)
assertEquals(VALID_AZURE_B2C_OIDC_CONFIG["token_endpoint"], clientRegistration.providerDetails.tokenUri)
assertEquals(issuer, clientRegistration.providerDetails.issuerUri)
assertEquals(issuer, clientRegistration.clientName)
val clientRegistration = { clientRegistrationBuilder.buildWithIssuerConfig(organization) }
expect {
that(clientRegistration()).and {
get { registrationId }.isEqualTo(URI.create(issuer).host)
get { providerDetails.userInfoEndpoint.userNameAttributeName }.isEqualTo("name")
get { authorizationGrantType }.isEqualTo(AuthorizationGrantType.AUTHORIZATION_CODE)
get { redirectUri }.isEqualTo("{baseUrl}/{action}/oauth2/code/{registrationId}")
get { providerDetails.authorizationUri }
.isEqualTo(VALID_AZURE_B2C_OIDC_CONFIG["authorization_endpoint"] as String?)
get { providerDetails.configurationMetadata }.isEqualTo(VALID_AZURE_B2C_OIDC_CONFIG)
get { providerDetails.tokenUri }.isEqualTo(VALID_AZURE_B2C_OIDC_CONFIG["token_endpoint"] as String?)
get { providerDetails.issuerUri }.isEqualTo(issuer)
get { clientName }.isEqualTo(issuer)
get { scopes }.containsExactlyInAnyOrder(AZURE_B2C_SCOPES)
}
}
}

@ParameterizedTest(name = "build client registration throws 401 for {0}")
Expand Down Expand Up @@ -349,6 +360,7 @@ internal class AuthenticationUtilsTest {
private const val OIDC_CONFIG_PATH = "/.well-known/openid-configuration"
private const val USER_ID = "userId"
private const val AZURE_B2C_ISSUER = "https://tenant.b2clogin.com/tenant.onmicrosoft.com/policy/v2.0"
private val AZURE_B2C_SCOPES = listOf("openid", "profile", "offline_access", CLIENT_ID)
private val UNVERSIONED_AZURE_B2C_ISSUER = AZURE_B2C_ISSUER.removeVersionSegment()
private val ORGANIZATION = Organization(ORGANIZATION_ID)
private val wireMockServer = WireMockServer(WireMockConfiguration().dynamicPort()).apply {
Expand Down

0 comments on commit 751cb3f

Please sign in to comment.