My other posts
Azure B2C Single Sign On for Atlassian
Table of contents
Introduction
I recently had to work on configuring an Azure B2C tenant to work with Atlassian SAML Single Sign On. It is an understatement to say that the documentation on both Microsoft's and Atlassian's side is beyond terrible. It did not help that most Q&As on both sides ended up unanswered! I finally managed to get it to work properly, and hopefully this will help others.
Just to be clear upfront:
- This was only tested on Atlassian Cloud.
- This works for both Jira Service Management Portal-only consumers and standard Atlassian Single Sign On.
- This post is NOT for standard Entra ID (Azure Active Directory) SSO.
Prerequisites
I will assume that you have access to all of the following:
- An Azure B2C tenant that uses Custom Policies. I have a sample solution that you can refer to.
- An Atlassian Cloud account with a subscription to Atlassian Guard (Standard or Premium).
- Administrator role in the Atlassian account, and either
B2C IEF Policy Administrator
andB2C IEF Keyset Administrator
, orGlobal Administrator
on the B2C tenant.
Variables
Please note that I will refer to the following variables throughout the post to make it easier to figure out what values you are looking for.
{B2C_TENANT}
: the name of your B2C tenant without the .onmicrosoft.com suffix.{SP_ENTITY_URL}
: the unique Service Provider Entity URL that Atlassian generates when you start a new SAML configuration. This has the format ofhttps://auth.atlassian.com/saml/{random ID}
.{SP_ACS_URL}
: the unique Service Provider Assertion Consumer Service URL that Atlassian generates when you start a new SAML configuration.- For Atlassian SSO, this is a URL under
https://auth.atlassian.com/
. - For Jira Service Management Portal-only consumer SSO, this is a URL akin to
https://id.atlassian.com/v2/customer/login/saml/callback?connection=saml-{random ID}
- For Atlassian SSO, this is a URL under
Prepare the certificate
This can be done using a certificate by a Certificate Authority (CA) or with a self-signed certificate. To use a self-signed certificate, follow the steps documented in this Microsoft Learn article.
Either way, you will need to export the certificate a second time. This time, export just the public key and in Base-64 format.
After that is done, upload the PFX certificate to B2C, as documented in the same Microsoft Learn article.
Initial Atlassian Configuration
The first step is to define whether you want this configuration to be applied to Portal-only users (Jira Service Management), to product users (employees, contractors, etc.), or both. If you want this to be used for both, you will need to follow these instructions twice, once for each.
For Atlassian users, follow these steps:
- Go to admin.atlassian.com. Select your organization if you have more than one.
- Select Security > Identity providers.
- Select your identity provider Directory.
- Select Set up SAML single sign-on.
- Add SAML details.
- Save SAML configuration.
If you want to configure this for Jira Service Management Portal-only users, follow these steps instead:
- From your organization at admin.atlassian.com, select Products.
- Under Sites and products, select the site you want to configure the SAML SSO for.
- Under Jira Service Management, select Portal-only customers.
- Select (more option) > Identity providers.
- Select Set up SAML single sign-on.
- Add SAML details.
- Select Save.
For both approaches, use these values:
- Identity provider Entity ID: use any value, this will be changed later.
- Identity provider SSO URL: use any value, this will be changed later.
- Public x509 Certificate: open the public key exported in Base-64 format (in Windows, this is a .CER file) with a text reader tool such as Notepad, and copy the entire content.
Trust Framework Extensions
First, you need to update your TrustFrameworkExtensions policy to enable responding to SAML requests.
Add the following TechnicalProfile
s to a ClaimsProvider
:
<TechnicalProfile Id="Saml2AssertionIssuer">
<DisplayName>SAML Issuer</DisplayName>
<Protocol Name="SAML2"/>
<OutputTokenFormat>SAML2</OutputTokenFormat>
<Metadata>
<Item Key="IssuerUri">{SP_ENTITY_URL}</Item>
<Item Key="NameIdPolicyFormat">NameID</Item>
</Metadata>
<CryptographicKeys>
<Key Id="SamlAssertionSigning" StorageReferenceId="B2C_1A_SamlIdpCert"/>
<Key Id="SamlMessageSigning" StorageReferenceId="B2C_1A_SamlIdpCert"/>
</CryptographicKeys>
<InputClaims/>
<OutputClaims/>
<UseTechnicalProfileForSessionManagement ReferenceId="SM-Saml-issuer"/>
</TechnicalProfile>
<!-- Session management technical profile for SAML-based tokens -->
<TechnicalProfile Id="SM-Saml-issuer">
<DisplayName>Session Management Provider</DisplayName>
<Protocol Name="Proprietary" Handler="Web.TPEngine.SSO.SamlSSOSessionProvider, Web.TPEngine, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null"/>
</TechnicalProfile>
Notice the use of SP_ENTITY_URL
in the block above. Open the corresponding Atlassian page where you configured SSO, and look at the value
it shows for the field Service provider entity URL
. Use that value instead of {SP_ENTITY_URL}
in the code above.
SAML RelyingParty
Next, you need to create a new policy file to use the SAML issuer. The easiest is to start from a copy of your SignUpSignIn policy. The end result should look like this:
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<TrustFrameworkPolicy
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns="http://schemas.microsoft.com/online/cpim/schemas/2013/06"
PolicySchemaVersion="0.3.0.0"
TenantId="{B2C_TENANT}.onmicrosoft.com"
PolicyId="B2C_1A_signup_signin_saml"
PublicPolicyUri="http://{B2C_TENANT}.onmicrosoft.com/B2C_1A_signup_signin_saml">
<BasePolicy>
<TenantId>{B2C_TENANT}.onmicrosoft.com</TenantId>
<PolicyId>B2C_1A_TrustFrameworkExtensions</PolicyId>
</BasePolicy>
<UserJourneys>
<UserJourney Id="SignUpSignIn" DefaultCpimIssuerTechnicalProfileReferenceId="Saml2AssertionIssuer">
<OrchestrationSteps>
<OrchestrationStep Order="7" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="Saml2AssertionIssuer"/>
</OrchestrationSteps>
</UserJourney>
</UserJourneys>
<RelyingParty>
<DefaultUserJourney ReferenceId="SignUpSignIn" />
<TechnicalProfile Id="PolicyProfile">
<DisplayName>PolicyProfile</DisplayName>
<Protocol Name="SAML2"/>
<Metadata>
<Item Key="PartnerEntity"><![CDATA[<?xml version="1.0"?><EntityDescriptor entityID="{SP_ENTITY_URL}" xmlns="urn:oasis:names:tc:SAML:2.0:metadata">
<SPSSODescriptor AuthnRequestsSigned="false" WantAssertionsSigned="false" protocolSupportEnumeration="urn:oasis:names:tc:SAML:2.0:protocol">
<SingleLogoutService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-Redirect" Location="https://auth.atlassian.com/saml/logout"/>
<AssertionConsumerService Binding="urn:oasis:names:tc:SAML:2.0:bindings:HTTP-POST" Location="{SP_ACS_URL}" index="1"/>
</SPSSODescriptor>
</EntityDescriptor>]]></Item>
</Metadata>
<OutputClaims>
<OutputClaim ClaimTypeReferenceId="displayName" />
<OutputClaim ClaimTypeReferenceId="givenName" />
<OutputClaim ClaimTypeReferenceId="surname" />
<OutputClaim ClaimTypeReferenceId="email" />
<OutputClaim ClaimTypeReferenceId="signInNames.emailAddress" PartnerClaimType="email" />
<OutputClaim ClaimTypeReferenceId="identityProvider" DefaultValue="" />
<OutputClaim ClaimTypeReferenceId="objectId" PartnerClaimType="objectId"/>
</OutputClaims>
<SubjectNamingInfo ClaimType="objectId" ExcludeAsClaim="true"/>
</TechnicalProfile>
</RelyingParty>
</TrustFrameworkPolicy>
Important notes:
- Be sure to replace the values for
{B2C_TENANT}
,{SP_ENTITY_URL}
, and{SP_ACS_URL}
, as appropriate. - Ensure that
SignUpSignIn
is the correct UserJourney in your environment. - There is an orchestration step with order 7 that replaces the OrchestrationStep with the same order in the base policy. Ensure that 7 is the correct value for the
SendClaims
step. - If you have any
SubJourney
of typeTransfer
, you will also need to replace the corresponding orchestration step(s). For example:<SubJourneys> <SubJourney Id="SignUp" Type="Transfer"> <OrchestrationSteps> <OrchestrationStep Order="5" Type="SendClaims" CpimIssuerTechnicalProfileReferenceId="Saml2AssertionIssuer"/> </OrchestrationSteps> </SubJourney> </SubJourneys>
After replacing all values, upload both policies to B2C and wait for the changes to apply (typically <15 minutes).
You will know this step is done when you can successfully load the following URL: https://{B2C_TENANT}.b2clogin.com/{B2C_TENANT}.onmicrosoft.com/B2C_1A_SIGNUP_SIGNIN_SAML/samlp/metadata
Of course, if you are using a custom domain, you can just as well use it: https://{CUSTOM_DOMAIN}/{CUSTOM_DOMAIN}/B2C_1A_SIGNUP_SIGNIN_SAML/samlp/metadata
.
Ensure that the value for entityID
in the top-level EntityDescriptor
element is the same value that you see for Service Provider Entity URL
on the Atlassian side.
Final Atlassian configuration
Once you have the metadata page loading and correctly configured (as above), go back to the Atlassian SAML configuration page, and edit the SAML configuration as follows:
- Identity provider Entity ID: use the value for
entityID
from the metadata page. - Identity provider SSO URL: use the value of the
Location
property for eitherSingleSignOnService
element.
Save the configuration.
Create the App Registration
The last step to do before testing the configuration, is to create an App Registration on the B2C tenant to support the redirection to Atlassian.
Create a standard App Registration, and use a Web redirect uri with the value of {SP_ENTITY_URL}
.
After that is created, you can test the SSO integration.
Closing
I hope that this detailed walkthrough helps you with the painful configuration required to get Atlassian SSO to work with B2C.
Feel free to reach out in the comments section for any help or tips!