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.
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.
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.
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.
....
$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.
$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.
$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:
$res = Invoke-WebRequest -Uri $fullurl -Header $hdrs
Examine the returned headers.
$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.
$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:
$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.
$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)
}