ZITADELZZITADEL
AnggakaraA
ZITADEL•2mo ago
Anggakara

Email is empty (EMAIL-spblu)

somehow cannot connect to zitadel after microsoft entraID redirection.
in this endpoint https://{{SELF_HOST}}/ui/login/login/externalidp/saml/acs we able to receive these data
  • http://schemas.microsoft.com/identity/claims/tenantid
  • http://schemas.microsoft.com/identity/claims/objectidentifier
  • http://schemas.microsoft.com/identity/claims/displayname
  • http://schemas.microsoft.com/identity/claims/identityprovider
  • http://schemas.microsoft.com/claims/authnmethodsreferences
  • http://schemas.microsoft.com/claims/authnmethodsreferences
  • http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname
  • http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname
  • http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress
  • http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name
but still receive the Email is empty (EMAIL-spblu) error.
already follow these tutorial but still have the error
  • https://zitadel.com/docs/guides/integrate/identity-providers/azure-ad-saml
  • https://youtu.be/1v5W42yznnY?si=d_9-85kcLXiiXm8N
ZITADELJoin
ZITADEL - Identity infrastructure, simplified for you.
4,316Members
View on Discord
Was this page helpful?
Powered by
Ask InkeepA
Ask Inkeep•10/20/25, 3:59 AM
Hi @Anggakara, I'm an AI assistant for ZITADEL. While a team member reviews this question, I'll see if I can find relevant sources for your question.
Ask InkeepA
Ask Inkeep•10/20/25, 4:00 AM

Answer



I can see you're receiving the email attribute from Entra ID correctly (`http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress%60) but ZITADEL is still showing an empty email error.

Based on the ZITADEL documentation, you can use an Action to properly map the Entra ID SAML attributes to ZITADEL user fields (1) (2) . Here's the exact code from the documentation for mapping Entra ID SAML attributes:
Ask InkeepA
Ask Inkeep•10/20/25, 4:00 AM
/**
 * Use SAML attributes from entra id SAML SP as attributes for user creation/update
 *
 * Flow: External Authentication, Trigger: Post authentication
 *
 * @param ctx
 * @param api
 */
function prefilRegisterFromEntraId(ctx, api) {
  if (ctx.v1.externalUser.externalIdpId != "<SAML SP id>") {
    return
  }
  // the attribute names below represent the crewjam IDP example, be sure to update them to match your provider info
  let firstname = ctx.v1.providerInfo.attributes["<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname%22%5D;>
  let lastname = ctx.v1.providerInfo.attributes["<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname%22%5D;>
  let email = ctx.v1.providerInfo.attributes["<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress%22%5D;>
  let displayname = ctx.v1.providerInfo.attributes["<http://schemas.microsoft.com/identity/claims/displayname%22%5D;>
  // username would look like this: adlerhurst_zitadel.com#EXT#@adlerhurstzitadel.onmicrosoft.com
  let username = ctx.v1.providerInfo.attributes["<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name%22%5D;>
  if (firstname != undefined) {
    api.setFirstName(firstname[0]) ;
  }
  if (lastname != undefined) {
    api.setLastName(lastname[0]) ;
  }
  if (email != undefined) {
    api.setEmail(email[0]) ;
    api.setEmailVerified(true) ;
  }
  if (displayname != undefined) {
    api.setDisplayName(displayname[0]) ;
  }
  if (username != undefined) {
    api.setPreferredUsername(username[0]) ;
  }
}
/**
 * Use SAML attributes from entra id SAML SP as attributes for user creation/update
 *
 * Flow: External Authentication, Trigger: Post authentication
 *
 * @param ctx
 * @param api
 */
function prefilRegisterFromEntraId(ctx, api) {
  if (ctx.v1.externalUser.externalIdpId != "<SAML SP id>") {
    return
  }
  // the attribute names below represent the crewjam IDP example, be sure to update them to match your provider info
  let firstname = ctx.v1.providerInfo.attributes["<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname%22%5D;>
  let lastname = ctx.v1.providerInfo.attributes["<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname%22%5D;>
  let email = ctx.v1.providerInfo.attributes["<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress%22%5D;>
  let displayname = ctx.v1.providerInfo.attributes["<http://schemas.microsoft.com/identity/claims/displayname%22%5D;>
  // username would look like this: adlerhurst_zitadel.com#EXT#@adlerhurstzitadel.onmicrosoft.com
  let username = ctx.v1.providerInfo.attributes["<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name%22%5D;>
  if (firstname != undefined) {
    api.setFirstName(firstname[0]) ;
  }
  if (lastname != undefined) {
    api.setLastName(lastname[0]) ;
  }
  if (email != undefined) {
    api.setEmail(email[0]) ;
    api.setEmailVerified(true) ;
  }
  if (displayname != undefined) {
    api.setDisplayName(displayname[0]) ;
  }
  if (username != undefined) {
    api.setPreferredUsername(username[0]) ;
  }
}
Ask InkeepA
Ask Inkeep•10/20/25, 4:00 AM
To implement this:

  1. Go to your target organization's settings page (1)
  2. Add a new action with the above code, ensuring the action name matches the function name
  3. Replace <SAML SP id><SAML SP id> with your actual SAML provider configuration ID
  4. Add the action to the "External Authentication" flow and trigger it on "Post Authentication"
This Action will properly map the email attribute from `http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress%60 to ZITADEL's email field and set it as verified (1) (2) .
Ask InkeepA
Ask Inkeep•10/20/25, 4:00 AM
​
To learn more, see the sources I considered:

[1] Configure Entra ID as a SAML Service Provider in ZITADEL
[2] Code examples
​
Ask InkeepA
Ask Inkeep•10/20/25, 4:00 AM
Feel free to tag @Ask Inkeep with additional questions.
Ask InkeepAAsk Inkeep​ To learn more, see the sources I considered: [[1] Configure Entra ID as a SAM...
Ask InkeepA
Ask InkeepAPP•10/20/25, 4:01 AM
Got it. Tagging @Rajat for additional help.
AnggakaraA
AnggakaraOP•10/20/25, 4:02 AM
your suggestion only applies if i use manual registration, but i use Automatic creation
image.png
AnggakaraA
AnggakaraOP•10/20/25, 9:39 AM
when i try to look into the SAML code, the email always return empty string. cmiiw

// GetEmail is an implementation of the [idp.User] interface.
func (u *UserMapper) GetEmail() domain.EmailAddress {
    return ""
}
// GetEmail is an implementation of the [idp.User] interface.
func (u *UserMapper) GetEmail() domain.EmailAddress {
    return ""
}
Screenshot_2025-10-20_163803.png
AnggakaraAAnggakarayour suggestion only applies if i use manual registration, but i use Automatic c...
MatíasM
Matías•10/20/25, 6:46 PM
Hi @Anggakara, thanks for reaching out! Using an Action to map user attributes from an external IdP is both for manual registration and for automatic creation. There are no predefined mappings, so unless you map the necessary user attributes using an Action, Automatic creation will fail with an error like the one you are receiving, complaining about a missing attribute or another.
The examples in the current documentation use Actions V1 for attribute mappings. This will work as long as you use the Login V1, but both will be deprecated in Zitadel V5.x.
The recommended approach moving forward is to use the new Login V2 along with Actions V2.
I hope that helps!
ZITADEL Docs
ZITADEL provides a hosted single-sign-on page to securely sign-in users to your applications.
AnggakaraA
AnggakaraOP•10/21/25, 4:58 AM
hi @Maik thanks a lot for the explanation now i am able to use the SSO SAML using v1. i'll try to use the v2 :zitadel:
Gigi the Giraffe (Zitadel)G
Gigi the Giraffe (Zitadel)•10/21/25, 5:01 AM
@Anggakara, you cannot mark your own questions as solved.
AnggakaraA
AnggakaraOP•10/21/25, 5:01 AM
whoops my bad 😂
Ask InkeepAAsk Inkeep​ To learn more, see the sources I considered: [[1] Configure Entra ID as a SAM...
Ask InkeepA
Ask InkeepAPP•10/21/25, 5:02 AM
Glad I could be helpful. Feel free to create a new thread with any new questions you may have.
AnggakaraA
AnggakaraOP•10/21/25, 2:37 PM
hi @Matías and @Rajat wanna do follow up question, i notice there are several endpoint that still not exist yet in the v2 like this
https://zitadel.com/docs/apis/resources/admin/admin-service-list-providers
the v2 only support the active one, not all idps list

may I know the mapping documentation from v1 to v2? or maybe you can explain me further
i plan to migrate all the endpoint in v1 to v2, but facing this issue. please kindly help
ZITADEL Docs
Returns a list of identity providers (social/enterprise login) configured on an instance level. e.g Google, AzureAD, etc.
MatíasM
Matías•10/21/25, 6:42 PM
Hi @Anggakara, we are slowly migrating resources from the API V1 into API V2 but as you've noticed, some endpoints haven't been migrated just yet. The migration plan is documented here. What I would recommend is to keep an eye on the release notes for a new major version before upgrading Zitadel to understand which resources have been migrated, and which endpoints will be deprecated, e.g.: https://github.com/zitadel/zitadel/releases/tag/v4.0.0
That way you can plan your migration accordingly.
GitHub
Release v4.0.0 · zitadel/zitadel
4.0.0 (2025-07-31)
Highlights
API v2: The New Resource-Based Standard
With this release, we&#39;ve completed the migration of core resources – including instances, organizations, projects, applicat...
AnggakaraA
AnggakaraOP•10/22/25, 9:10 AM
thanks a lot for the explanation @Matías
<SAML SP id>
<SAML SP id>
/**
 * Use SAML attributes from entra id SAML SP as attributes for user creation/update
 *
 * Flow: External Authentication, Trigger: Post authentication
 *
 * @param ctx
 * @param api
 */
function prefilRegisterFromEntraId(ctx, api) {
  if (ctx.v1.externalUser.externalIdpId != "<SAML SP id>") {
    return
  }
  // the attribute names below represent the crewjam IDP example, be sure to update them to match your provider info
  let firstname = ctx.v1.providerInfo.attributes["<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname%22%5D;>
  let lastname = ctx.v1.providerInfo.attributes["<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname%22%5D;>
  let email = ctx.v1.providerInfo.attributes["<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress%22%5D;>
  let displayname = ctx.v1.providerInfo.attributes["<http://schemas.microsoft.com/identity/claims/displayname%22%5D;>
  // username would look like this: adlerhurst_zitadel.com#EXT#@adlerhurstzitadel.onmicrosoft.com
  let username = ctx.v1.providerInfo.attributes["<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name%22%5D;>
  if (firstname != undefined) {
    api.setFirstName(firstname[0]) ;
  }
  if (lastname != undefined) {
    api.setLastName(lastname[0]) ;
  }
  if (email != undefined) {
    api.setEmail(email[0]) ;
    api.setEmailVerified(true) ;
  }
  if (displayname != undefined) {
    api.setDisplayName(displayname[0]) ;
  }
  if (username != undefined) {
    api.setPreferredUsername(username[0]) ;
  }
}
/**
 * Use SAML attributes from entra id SAML SP as attributes for user creation/update
 *
 * Flow: External Authentication, Trigger: Post authentication
 *
 * @param ctx
 * @param api
 */
function prefilRegisterFromEntraId(ctx, api) {
  if (ctx.v1.externalUser.externalIdpId != "<SAML SP id>") {
    return
  }
  // the attribute names below represent the crewjam IDP example, be sure to update them to match your provider info
  let firstname = ctx.v1.providerInfo.attributes["<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/givenname%22%5D;>
  let lastname = ctx.v1.providerInfo.attributes["<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/surname%22%5D;>
  let email = ctx.v1.providerInfo.attributes["<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress%22%5D;>
  let displayname = ctx.v1.providerInfo.attributes["<http://schemas.microsoft.com/identity/claims/displayname%22%5D;>
  // username would look like this: adlerhurst_zitadel.com#EXT#@adlerhurstzitadel.onmicrosoft.com
  let username = ctx.v1.providerInfo.attributes["<http://schemas.xmlsoap.org/ws/2005/05/identity/claims/name%22%5D;>
  if (firstname != undefined) {
    api.setFirstName(firstname[0]) ;
  }
  if (lastname != undefined) {
    api.setLastName(lastname[0]) ;
  }
  if (email != undefined) {
    api.setEmail(email[0]) ;
    api.setEmailVerified(true) ;
  }
  if (displayname != undefined) {
    api.setDisplayName(displayname[0]) ;
  }
  if (username != undefined) {
    api.setPreferredUsername(username[0]) ;
  }
}
// GetEmail is an implementation of the [idp.User] interface.
func (u *UserMapper) GetEmail() domain.EmailAddress {
    return ""
}
// GetEmail is an implementation of the [idp.User] interface.
func (u *UserMapper) GetEmail() domain.EmailAddress {
    return ""
}