s-KaiNet / node-sp-auth

Unattended SharePoint http authentication with nodejs
MIT License
137 stars 55 forks source link

"No XML to parse!" when trying to authenticate "Federated" user #90

Open jampy opened 3 years ago

jampy commented 3 years ago

I'm trying to access a SharePoint site I have no control over (and I'm having a hard time understanding the whole SharePoint stuff).

node-sp-auth works for some users, but it just throws an error "No XML to parse!" for others. I think this problem is related to #62

I was able to track this down to node-sp-auth/lib/src/utils/AdfsHelper.js where request.post() just returns an empty string in xmlResponse.

When node-sp-auth loads userRealm from the OnlineUserRealmEndpoint in OnlineUserCredentials.js I can see that the two kinds of users are completely different.

This one does authenticate just fine:

userRealm:
   { State: 4,
     UserState: 1,
     Login: 'xxxxxxxxxxx@myemaildomain.it',
     NameSpaceType: 'Managed',
     DomainName: 'myemaildomain.it',
     FederationBrandName: 'myemaildomain.it',
     CloudInstanceName: 'microsoftonline.com',
     CloudInstanceIssuerUri: 'urn:federation:MicrosoftOnline' } 

This one, however, leads to the "No XML to parse!" as described:

 userRealm:
   { State: 3,
     UserState: 2,
     Login: 'xxxxx.xxxxxxxx@gmail.com',
     NameSpaceType: 'Federated',
     DomainName: 'live.com',
     FederationGlobalVersion: -1,
     AuthURL:
      'https://login.live.com/login.srf?username=xxxxxx.xxxxxxxxx%40gmail.com&wa=wsignin1.0&wtrealm=urn%3afederation%3aMicrosoftOnline&wctx=',
     FederationBrandName: 'Windows Live',
     CloudInstanceName: 'microsoftonline.com',
     CloudInstanceIssuerUri: 'urn:federation:MicrosoftOnline' }

I guess the different NameSpaceType is important.

Can I do anything to make the "Federated" user authenticate?

s-KaiNet commented 3 years ago

Hi,

it looks like the second login is a personal account, not organizational. Unfortunately, personal accounts are not supported.

jampy commented 3 years ago

Okay. Could you please explain to me what it would take to support personal accounts?

Is this a limitation of SharePoint (meaning that it does not allow unattended auth for such accounts) or a NodeJS limitation? Would it be possible, with some effort, to extend node-sp-auth for federated users?

s-KaiNet commented 3 years ago

That's a limitation of node-sp-auth library. TBH I don't have plans to add support, because in most cases all node-sp-auth clients are organizational users, they don't use personal accounts for authentication.

jampy commented 3 years ago

I see.

Could you please give me some directions where I can learn more about auth for personal accounts (or, at least, how to detect such users)?

So I could try to implement it myself or pay other developers to do it.

Thanks!

jampy commented 3 years ago

Nothing? :-(

I need to provide access to a Sharepoint site for a few hundred users and I'm having this problem with a lot of them (the vast majority).

s-KaiNet commented 3 years ago

Unfortunately, I don't have a precise answer to your question. I have experience only with organizational accounts in nodejs. And it was a few years ago. Could you describe what kind of application you're trying a build? Maybe there is another way of doing it?

jampy commented 3 years ago

There is an existing Sharepoint website (www.bauer.vip) of a local organization (not mine) hosting documents for about 1700 members. A good amount of these members are using my app (PWA) and that app should access the documents of the Sharepoint site to display the documents and to extract data from some of these documents.

Users currently enter their Sharepoint email address and password in my app which is then authenticated on their behalf and then using the REST interface to access the documents.

I've got most of it working and in fact first tests (with a test account that has been given to me) have been promising. A few days ago I invited many people to test that function, but currently the authentication fails for about all the real world accounts.

jampy commented 3 years ago

Perhaps I'm forced to do browser-based authentication? Can that be done with node-sp-auth somehow?

koltyakov commented 3 years ago

For such type of apps, which emerge in my head after reading it, OAuth2 Flow (with ADAL/MSAL) is preferable.

With server-side auth, there is a chance that 2Fa or CAS will be enabled then, and auth stop working at all. Also, the fact of grabbing and storing (even temporary in memory) user credentials might not pass an audit.

In a majority of cases, the library is used for those 3 scenarios:

In all of these either special attention is applied to a service account or a dev environment is used. Not saying that the library can't be used for the backend method in service with passing user creds but in 90% of the cases, I'd personally avoid sending user creds over the wires, especially federated accounts which are not owned by a system.

s-KaiNet commented 3 years ago

I fully agree, OAuth2 Flow is the best option here.

jampy commented 3 years ago

Thanks @koltyakov for your reply.

Could you kindly provide a link for me where I could start learning how to implement the OAuth2 Flow you mentioned? Is a there a suitable NPM package available?

Please don't think that I'm too lazy to search myself. I have implemented lots of interfaces to other systems in the past but Sharepoint with all it's flavours and options is overwhelming me so that I'm having a hard time to understand what route I should go, so giving me concrete directions/hints would greatly be appreciated...

Thanks a lot!

koltyakov commented 3 years ago

I'd check https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-browser and a corresponding wrapper for React or Angular or a custom one.

The main challenge might be configuring AAD correctly, this article could help https://www.linkedin.com/pulse/building-single-page-application-react-msaljs-pnpjs-sergei-sergeev/

With PnPjs, https://pnp.github.io/pnpjs/authentication/msaljsclient/ this one to follow.

michaelmaillot commented 2 years ago

Hi,

I also encounter the same error but in a organizational context, which is SharePoint Online with an ADFS server.

The problem in my case is related to the ADFS. Even if the related Wiki states that the library automatically detects and use the company ADFS server, I noticed that in the source code it's static:

export class AdfsHelper {
  public static getSamlAssertion(credentials: IAdfsUserCredentials): Promise<SamlAssertion> {
    const adfsHost: string = url.parse(credentials.adfsUrl).host;
    const usernameMixedUrl = `https://${adfsHost}/adfs/services/trust/13/usernamemixed`;

    const samlBody: string = template(adfsSamlWsfedTemplate)({
      to: usernameMixedUrl,
      username: credentials.username,
      password: credentials.password,
      relyingParty: credentials.relyingParty
    });

    return request.post(usernameMixedUrl, {
      body: samlBody,
      resolveBodyOnly: true,
      headers: {
        'Content-Length': samlBody.length.toString(),
        'Content-Type': 'application/soap+xml; charset=utf-8'
      }
    })

It looks like I'm redirected to the on-prem scenario even if the target is SharePoint Online.

When querying the getuserrealm.srf page with my login, I got the followning info:

<RealmInfo Success="true">
<State>3</State>
<UserState>2</UserState>
<Login>michael.maillot@company.com</Login>
<NameSpaceType>Federated</NameSpaceType>
<DomainName>company.com</DomainName>
<FederationGlobalVersion>-1</FederationGlobalVersion>
<AuthURL>https://adfs-gateway.com/WSFed/ls/O365?username=michael.maillot%40company.com&wa=wsignin1.0&wtrealm=urn%3afederation%3aMicrosoftOnline&wctx=</AuthURL>
<IsFederatedNS>true</IsFederatedNS>
<STSAuthURL>https://adfs-gateway.com/WSFed/usernamemixed/O365</STSAuthURL>
<FederationTier>0</FederationTier>
<FederationBrandName>Company Name</FederationBrandName>
<AllowFedUsersWLIDSignIn>false</AllowFedUsersWLIDSignIn>
<Certificate>0a1b2c3d4e5f6g7h8i9j</Certificate>
<PreferredProtocol>1</PreferredProtocol>
<EDUDomainFlags>0</EDUDomainFlags>
<CloudInstanceName>microsoftonline.com</CloudInstanceName>
<CloudInstanceIssuerUri>urn:federation:MicrosoftOnline</CloudInstanceIssuerUri>
</RealmInfo>

According to me, I think that the library should use the "AuthURL" to get the SAML assertion, instead of the static value usernameMixedUrl, when it's about SPO + ADFS.

Or maybe am I missing something?

zubairk14 commented 1 year ago

@michaelmaillot I'm running into the same exact problem with authenticating via AFDS for SPO - hoping you found a solution in the past 6 months 🤞 ... if you did, would love if you could share!

michaelmaillot commented 1 year ago

Hi @zubairk14 I've left this behind to focus on other business stuff since, sorry 😕

But I was hoping an answer from community folks, to see if my assumptions were right and if it's the case, I could propose a PR.

@s-KaiNet or @koltyakov would you mind giving your thoughts regarding this?