github / platform-samples

A public place for all platform sample projects.
Creative Commons Zero v1.0 Universal
1.91k stars 1.74k forks source link

Add query that lists SAML external identities for an Organization #168

Open osowskit opened 6 years ago

osowskit commented 6 years ago

Business Plan hosted customers with SAML would like a way to programmatically query information about a GitHub user account's SAML identity. The following is a GraphQL query that accomplishes this for an Organization.

query {
  organization(login: "LOGIN") {
    samlIdentityProvider {
      ssoUrl
      externalIdentities(first: 100) {
        edges {
          node {
            guid
            samlIdentity {
              nameId
            }
            user {
              login
            }
          }
        }
      }
    }
  }
}

/cc @francisfuzz as you might be able to track down the author to see if they want to add this ☝️

mikesimons commented 5 years ago

This query was super helpful for us as we're primarily using v3 and couldn't see a way to do this there. I found the token needs admin:org though which feels excessive for a read only query.

AjkayAlan commented 4 years ago

This query was also super helpful for us - v3 provides their SCIM endpoint (https://developer.github.com/v3/scim/) which gets us close, but does not provide a true way to tie a GitHub identity to a SAML identity.

Using this query in v4 we are able to pull our user's verified domain email addresses and associate it to their github handle, making our notification process a lot easier.

kamaltejaaol commented 4 years ago

@AjkayAlan, I'm testing on the same where the email address for SAML authentication is different from github registered email address, can you please share us the query to trigger in GraphQL

AjkayAlan commented 4 years ago

@kamaltejaaol Unfortunately my org policy does not allow me to share code easily, specifically code I developed for our organization.

What we ended up doing was using the query provided by @osowskit above to get the SAML nameId, which corresponded to our user's primary email address associated to their enterprise account (the same address we would send emails on). Additionally, the query above gets you the user login, which is the actual github login of the associated user.

From there, you can just make a key value pair list of github login to enterprise email address. Once you have that, you should be off to the races.

In our case, we had to revoke PAT and SSH keys over a specified age limit using the credential authorizations API (https://developer.github.com/v3/orgs/#list-credential-authorizations-for-an-organization). That returns the github login, which we then look up against the list of login/email kvp's. From there, we were able to notify the users in advance that they needed to rotate their credentials to avoid disruption.

kamaltejaaol commented 4 years ago

@AjkayAlan, i understand the corporate security policies, but thanks for giving the detailed explanation, i will try to work it out. Thanks again.

gracevivi523 commented 4 years ago

Hi, @osowskit Thanks for this solution, really saved my day, however I am getting null with this query, not sure what I am missing here possibly? I have correct scope for the token and header stuff.

below is the return. { "data": { "organization": { "samlIdentityProvider": null } } }

osowskit commented 4 years ago

@swinton @mtodd ☝️

gracevivi523 commented 4 years ago

Thanks @osowskit .

@swinton @mtodd , I am still stuck on it, any help will be highly appreciated.

gracevivi523 commented 4 years ago

Just did a bit more research on the issue, could it be related to SCIM is not enabled?

micimize commented 3 years ago

I am experiencing the same {"data":{"organization":{"samlIdentityProvider":null}}} issue making requests with both

So I'm wondering:

  1. My account is an org owner, but (I believe) only an enterprise member. Could this be the source of the issue?
  2. If it is, shouldn't at least enterprise.ownerInfo.samlIdentityProvider throw an error?
  3. More generally, what are the constraints and necessary permissions for this API?
  4. What are its the supported consumers, and do they have varied constraints?
  5. Are there known gotchas and caveats such as what @gracevivi523 proposed?

Also, this query has been added to the examples.

micimize commented 3 years ago

What I've learned since the above:

kamaltejaaol commented 3 years ago

https://gist.github.com/gbaman/b3137e18c739e0cf98539bf4ec4366ad ... these details helped me to develop python code to get the saml identify value and generate report

barakwei commented 3 years ago

I also got {"data":{"organization":{"samlIdentityProvider":null}}} when querying the organization, that's because the SAML configuration was at the enterprise level, so to get that the following query helped.

{
  enterprise(slug: "Your enterprise here") {
    ownerInfo {
      samlIdentityProvider {
        externalIdentities(after: null, first: 100) {
          pageInfo {
            hasNextPage
            endCursor
          }
          edges {
            node {
              user {
                login
              }
              samlIdentity {
                nameId
              }
            }
          }
        }
      }
    }
  }
}
joehorsnell commented 3 years ago

For anyone interested in how to adapt the original query from the first comment to support pagination, after some playing around and reading the docs (I'm not too familiar with GraphQL, so this wasn't obvious to me), I got this (example below using the GitHub CLI, see also here):

gh api graphql --paginate -f query='
query($endCursor: String) {
  organization(login: "<name-of-org>") {
    samlIdentityProvider {
      ssoUrl,
      externalIdentities(first: 100, after: $endCursor) {
        edges {
          node {
            guid,
            samlIdentity {
              nameId
            }
            user {
              login
            }
          }
        }
        pageInfo{
          hasNextPage,
          endCursor
        }
      }
    }
  }
}
'
alex-mozejko commented 3 years ago

for anyone who comes here looking for a way to find the organisational email for a particular user, it is:

query {
  user(login: "SOME-USER"){
    organizationVerifiedDomainEmails(login: "SOME-ORG")
  }
}
genio commented 2 years ago

Is there no way to get this information with a REST API endpoint?

threesquared commented 2 years ago

So I wanted to find the GH username for a specific SAML email address and didn't want to have to paginate and loop through the results so this worked for me:

organization(login: "org") {
  samlIdentityProvider {
    externalIdentities(first: 100, userName: "user.email@domain.com") {
      edges {
        node {
          user {
            login
          }
          samlIdentity {
            nameId
          }
        }
      }
    }
  }
}
dylan-asos commented 2 years ago

Thanks all - this helped me out.

For anyone working with c# who gets here, here's a gist https://gist.github.com/dylan-asos/091f2b8e6a865538f061f7554fc03566 - give it an email, it'll give you the matching username/login.

I'm being overly verbose to show some of the moving parts, you could switch out some of that with parsing libs of your choice.

EdEastman commented 2 years ago

I'm trying this but we have SAML enabled at Enterprise level and when we try these or similar queries I get this:

The Organization's SAML identity provider is disabled when an Enterprise SAML identity provider is available.

I can't find any similar option to get this information at the enterprise level. Any ideas on how to get that info?

barakwei commented 2 years ago

@EdEastman see my comment above

EdEastman commented 2 years ago

Gah, sorry, now I'm the guy that didn't read the thread :( oh well hopefully at least that error message will be indexed now...many thanks @barakwei - that works perfectly

DushanthaS commented 1 year ago

for anyone who comes here looking for a way to find the organisational email for a particular user, it is:

query {
  user(login: "SOME-USER"){
    organizationVerifiedDomainEmails(login: "SOME-ORG")
  }
}

Thanks ,Here is the query with variables

query($user: String!, $org:String!) { user(login: $user){ organizationVerifiedDomainEmails(login: $org) } }

{ "user": "SOME-USER", "org": "SOME-ORG" }

mikheyevav commented 11 months ago

Hello, With enterprise-level SAML I get "ownerInfo": null for the following query:

{
  enterprise(slug: "Your enterprise here") {
    ownerInfo {
      samlIdentityProvider {
        id
      }
    }
}

I still can see the sso email through web interface though. Please help

Posrabi commented 7 months ago

Has anyone got this to work on enterprise server? I can get it to work on cloud but not server (self-hosted). I'm just getting:

{"data":{"enterprise":{"ownerInfo":{"samlIdentityProvider":null}}}}
sandeepprasai commented 4 months ago

All you need is read scope permissions now. https://github.blog/changelog/2023-03-30-samlidentity-graphql-object-now-supports-read-scope/