Using Bearer Tokens to access identity provider APIs

This article describes how to use a bearer token returned with the Get-1EBearerToken cmdlet to directly call identity provider APIs. This can be very useful for testing.

Recent versions of the 1E PowerShell Toolkit now support obtaining a bearer token interactively as well as via a signed JWT. This can be convenient for testing. Note however that for this to work, you must add a redirect URL to the identity provider application used for interactive authentication, and the functionality is intended for test purposes only.
For more information see the get-1ebearertoken cmdlet.

Example 1: Retrieving a list of groups from Okta

Assume that an application has been created for non-interactive authentication in an Okta instance, and that the application was granted okta.groups.manage scope, which allows it to fully manage groups. We can read a list of groups from Okta as shown below.

Copy
import-module .\ps1etoolkit.psd1 -force
$t = get-1ebearertoken -appid 0oadaw9ppnhRDGHE25d7 -certsubject cn=corellian -metadataendpoint https://dev-74583794.okta.com/.well-known/openid-configuration  -scope "okta.groups.manage"
$header = "Bearer " + $t.access_token
$hdrs =  @{
       Authorization = $header
      }
$urlbase = "https://dev-74583794.okta.com/api/v1"
$api = "/groups/?q=a&limit=20000"
$fullurl = $urlbase + $api
$res = Invoke-RestMethod -Method GET -Uri $fullurl -Header $hdrs 
write-host $res

In the example, the Corellian certificate has been used on the assumption that it was uploaded to the application associated with the Okta instance. We then get a bearer token and then create an Authorization header whose value is “Bearer <token>”. Finally, we call the Okta groups API to retrieve a list of groups.

Example 2: Adding a group to Okta

Note that you need to add headers to specify accepted and sent content type. You can easily use this example to create a large set of groups for testing.

Copy
import-module .\ps1etoolkit.psd1 -force
$t = get-1ebearertoken -appid 0oadaw9ppnhRDGHE25d7 -certsubject cn=corellian -metadataendpoint https://dev-74583794.okta.com/.well-known/openid-configuration  -scope "okta.groups.manage"
$header = "Bearer " + $t.access_token
$hdrs =  @{
       Authorization = $header
      Accept = "application/json"
      "Content-Type" = "application/json"
      }
$urlbase = "https://dev-74583794.okta.com/api/v1"
$api = "/groups"
$fullurl = $urlbase + $api
$payload = @"
{
  "profile":
  {
  "name": "AJM Test Group",
  "description": "AJM Test Group"
  }
}
"@
$res = Invoke-RestMethod -Method POST -Uri $fullurl -Header $hdrs -Body $payload
write-host $res

Example 3: Adding a set of groups to Okta

This code snippet excludes the common setup steps shown in the previous examples, which should be included at the start of the script.

It will then create 299 groups called AJM Test Group 1….. AJM Test Group 299.

The sleep 1 is just to avoid triggering Okta test instance rate limiting and may not be necessary.

Copy
for ($i = 1;$i -lt 300; $i++)
    {
    $grpname = "AJM Test Group " + $i
    $payload = @"
    {
      "profile":
      {
      "name": "$($grpname)",
      "description": "$($grpname)"
      }
    }
"@
$res = Invoke-RestMethod -Method POST -Uri $fullurl -Header $hdrs -Body $payload
write-host $res
sleep 1
    }

Example 4: Adding a user to a large set of groups in Okta

The example code below assigns a user called user@manygroups.org to the groups created in the previous example. Again, the standard setup has been omitted for brevity. At the end of this script, the user will belong to all 299 groups.

The get-1ebearertoken cmdlet must request the okta.users.read scope as well as okta.groups.manage and the associated app must have both scopes granted. The amended cmdlet parameter is shown in the example below but with the rest of the standard setup omitted.

Copy
....
$t = get-1ebearertoken -appid 0oadaw9ppnhRDGHE25d7 -certsubject cn=corellian -metadataendpoint https://dev-74583794.okta.com/.well-known/openid-configuration  -scope "okta.groups.manage okta.users.read"
....
$api = "/users?search=profile.login%20eq%20%22user@manygroups.org%22"
$fullurl = $urlbase + $api
$user = Invoke-RestMethod -Method GET -Uri $fullurl -Header $hdrs
$userid = $user.id
write-host $user
$api = "/groups/?q=AJM%20Test%20Group&limit=20000"
$fullurl = $urlbase + $api
$grps = Invoke-RestMethod -Method GET -Uri $fullurl -Header $hdrs
foreach ($grp in $grps)
    {
    $api = "/groups/" + $grp.id + "/users/" + $userId
    $fullurl = $urlbase + $api
    $res = Invoke-RestMethod -Method PUT -Uri $fullurl -Header $hdrs
    write-host $res
    sleep 1
    }

Example 5: Getting the user’s group membership from Okta

When we run this and look at the result in $res, we find that although we thought the user was a member of 299 groups, only 200 were returned.

Copy
$api = "/users?search=profile.login%20eq%20%22user@manygroups.org%22"
$fullurl = $urlbase + $api
$user = Invoke-RestMethod -Method GET -Uri $fullurl -Header $hdrs
$userid = $user.id
$api = "/users/" + $userid + "/groups"
$fullurl = $urlbase + $api
$res = Invoke-RestMethod -Method GET -Uri $fullurl -Header $hdrs

Example 6: Cross-checking the user’s group membership in Okta

We iterate through the groups and check the members of each group. This shows that the user is in fact a member of all 299 groups.

Copy
$api = "/groups/?q=AJM%20Test%20Group&limit=20000"
$fullurl = $urlbase + $api
$grps = Invoke-RestMethod -Method GET -Uri $fullurl -Header $hdrs
$i = 0
foreach ($grp in $grps)
    {
    $api = "/groups/" + $grp.id + "/users"
    $fullurl = $urlbase + $api
    $usersInGroup = Invoke-RestMethod -Method GET -Uri $fullurl -Header $hdrs
    foreach ($user in $usersInGroup)
        {
        write-host $i " " $grp.id  " "  $user.profile.lastName
        }
    $i++
    sleep 1
    }

Example 7: Retrieving Okta’s pagination headers

Okta returns a special response header to indicate that more results are available. To see this, modify Example 5 so that the last line reads the following:

Copy
$res = Invoke-WebRequest -Uri $fullurl -Header $hdrs

Examine the returned headers.

Copy
$res.Headers.link
<https://dev-74583794.okta.com/api/v1/users/00udb0dsrlyXv3ycl5d7/groups?limit=200>; rel="self",<https://dev-74583794.okta.com/api/v1/users/00udb0dsrlyXv3ycl5d7/groups?after=00gdb07dmnmOXk7Ao5d7&limit=200>; rel="next"

If there is a value rel=”next”, then there are more results to return.

To retrieve the remaining results, we must call the API and include the parameter ?after as shown below.

Copy
$fullurl = $fullurl + "?after=00gdb07dmnmOXk7Ao5d7&limit=200"
$res = Invoke-WebRequest -Uri $fullurl -Header $hdrs

When we do this and examine the response headers, we will see the following:

Copy
$res.Headers.link
<https://dev-74583794.okta.com/api/v1/users/00udb0dsrlyXv3ycl5d7/groups?after=00gdb07dmnmOXk7Ao5d7&limit=200>; rel="self"

We see that there is no rel=”next” value, so we have retrieved all the values.

Example 8: Returning all the groups using the pagination headers

We can now correct the issue by using the pagination headers. We perform the initial query and then check whether the response headers contain rel=”next”. If they do, we do a regex match to get the ?after parameter, and then we repeat the query until we no longer get a rel=”next” value. At this point we have all the groups.

Copy
$api = "/users?search=profile.login%20eq%20%22user@manygroups.org%22"
$fullurl = $urlbase + $api
$user = Invoke-RestMethod -Method GET -Uri $fullurl -Header $hdrs
$userid = $user.id
$api = "/users/" + $userid + "/groups"
$fullurl = $urlbase + $api
$usergrps = New-Object System.Collections.Generic.List[System.Object]
$res = Invoke-WebRequest -Uri $fullurl -Header $hdrs
$thesegrps = convertfrom-json $res.Content 
[void]$usergrps.AddRange($thesegrps)
while ($res.Headers.link.Contains('rel="next"'))
    {
    $link=$res.Headers.link -match "(\?after=.*?)>"
    $next=$matches[1]
    $url = $fullurl + $next
    $res = Invoke-WebRequest -Uri $url -Header $hdrs
    $thesegrps=convertfrom-json $res.Content
    [void]$usergrps.AddRange($thesegrps)
    }