Hello,
I need to list users and groups on a backend microservice that will call the Okta Api but I can’t use an API Token with SDK (simplest way) and I can’t use a key pair with client credentials too (Information Security area policies).
I would like to get a token for an administrator read only account passing username/password to get the token with the roles okta.users.read/okta.groups.read.
My application type is web.
I did something similar on Postman following this procedure describe on this page: Implement OAuth for Okta | Okta Developer
Using Postman a login page was prompted to me and I put username and password. I can’t do that in my code because I trying to do a microservice that will call the okta users/groups api with a token generated by an oauth2 flow with the caveats that I described above.
I blowing up my mind with this issue because I didn’t found any oauth2 flow that works this way.
I appreciate any tip. I am using spring boot application.
phi1ipp
September 4, 2021, 2:01pm
2
There is a trick for that:
You run /authn request with your username and password. On SUCCESS you’ll get sessionToken back
You run /authorize request with ...prompt=none&sessionToken=<your_token>&response_type=token... and all other parameters matching your OIDC app configuration (client_id, scope, redirect_uri). If all was success, then you’ll get a redirection response with URL having access token in it.
If your app is PKCE or web you’ll have to do an extra step with code exchange. I haven’t tried it myself, but you can try and let us all know if it also works for you
Hello Phi1ipp,
The application type is web. I did what you suggested but when I called the /authorize request I got in response a page like this:
Login with OAuth 2.0
[invalid_request]
I think someone had this situation before. I can research something similar on github, but I don’t know exactly what I looking for, how do we named this flow? How would you do this research?
phi1ipp
September 6, 2021, 5:31pm
5
you need to send correct values to your endpoints. I just ran my example with “web” flow, and all worked just fine
call to /autorize → you get the code
call to /token with the code from #1 and authorization from client id/secret → you get your token
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/url"
"regexp"
"strings"
)
const baseUrl = "https://dev-xxxxx.oktapreview.com"
const client_secret = "<app client secret>"
const client_id = "<app client id>"
const redirect_uri = "http://localhost:8080/cb.html"
const scope = "openid"
const username = "<your username>"
const password = "<your password>"
func main() {
fmt.Println("starting")
vals := map[string]string{"username": username, "password": password}
json_data, err := json.Marshal(vals)
if err != nil {
fmt.Println("error encoding json")
return
}
resp, err := http.Post(fmt.Sprintf("%s/api/v1/authn", baseUrl), "application/json", bytes.NewBuffer(json_data))
if err != nil {
fmt.Println("error postin")
return
}
if resp.StatusCode != 200 {
fmt.Println("not 200")
return
}
var body map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&body)
if err != nil {
fmt.Println("error parsing response body")
return
}
var sessionToken string = body["sessionToken"].(string)
fmt.Println("stateToken: " + sessionToken)
authUrl :=
fmt.Sprintf("%s/oauth2/default/v1/authorize?client_id=%s&redirect_uri=%s&sessionToken=%s&scope=%s",
baseUrl, client_id, redirect_uri, sessionToken, scope)
const_part := "&prompt=none&response_type=code&response_mode&nonce=123&state=123"
client := &http.Client {
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
req, err := http.NewRequest("GET", authUrl + const_part, nil)
resp, err = client.Do(req)
if err != nil {
fmt.Println("error sending GET")
fmt.Println(err)
}
if resp.StatusCode != 302 {
fmt.Println("response is not 302")
return
}
fmt.Println("response is ", resp.Header["Location"][0])
r := regexp.MustCompile(`code=(?P<code>[^&]+)`)
m := r.FindStringSubmatch(resp.Header["Location"][0])
code := m[1]
fmt.Println("code:", code)
urlToken := fmt.Sprintf("%v/oauth2/default/v1/token", baseUrl)
formData := url.Values{
"code":{code},
"grant_type":{"authorization_code"},
"redirect_uri": {redirect_uri},
}
client = &http.Client{}
req, err = http.NewRequest("POST", urlToken, strings.NewReader(formData.Encode()))
if err != nil {
fmt.Println("error prepping req to /token")
fmt.Println(err)
}
req.SetBasicAuth(client_id, client_secret)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err = client.Do(req)
if err != nil {
fmt.Println("error posting data to /token")
fmt.Println(err)
}
err = json.NewDecoder(resp.Body).Decode(&body)
if err != nil {
fmt.Println("error decoding body from /token")
fmt.Println(err)
}
fmt.Println("access_token:", body["access_token"])
}
phi1ipp:
package main
import (
"bytes"
"encoding/json"
"fmt"
"net/http"
"net/url"
"regexp"
"strings"
)
const baseUrl = "https://dev-xxxxx.oktapreview.com"
const client_secret = "<app client secret>"
const client_id = "<app client id>"
const redirect_uri = "http://localhost:8080/cb.html"
const scope = "openid"
const username = "<your username>"
const password = "<your password>"
func main() {
fmt.Println("starting")
vals := map[string]string{"username": username, "password": password}
json_data, err := json.Marshal(vals)
if err != nil {
fmt.Println("error encoding json")
return
}
resp, err := http.Post(fmt.Sprintf("%s/api/v1/authn", baseUrl), "application/json", bytes.NewBuffer(json_data))
if err != nil {
fmt.Println("error postin")
return
}
if resp.StatusCode != 200 {
fmt.Println("not 200")
return
}
var body map[string]interface{}
err = json.NewDecoder(resp.Body).Decode(&body)
if err != nil {
fmt.Println("error parsing response body")
return
}
var sessionToken string = body["sessionToken"].(string)
fmt.Println("stateToken: " + sessionToken)
authUrl :=
fmt.Sprintf("%s/oauth2/default/v1/authorize?client_id=%s&redirect_uri=%s&sessionToken=%s&scope=%s",
baseUrl, client_id, redirect_uri, sessionToken, scope)
const_part := "&prompt=none&response_type=code&response_mode&nonce=123&state=123"
client := &http.Client {
CheckRedirect: func(req *http.Request, via []*http.Request) error {
return http.ErrUseLastResponse
},
}
req, err := http.NewRequest("GET", authUrl + const_part, nil)
resp, err = client.Do(req)
if err != nil {
fmt.Println("error sending GET")
fmt.Println(err)
}
if resp.StatusCode != 302 {
fmt.Println("response is not 302")
return
}
fmt.Println("response is ", resp.Header["Location"][0])
r := regexp.MustCompile(`code=(?P<code>[^&]+)`)
m := r.FindStringSubmatch(resp.Header["Location"][0])
code := m[1]
fmt.Println("code:", code)
urlToken := fmt.Sprintf("%v/oauth2/default/v1/token", baseUrl)
formData := url.Values{
"code":{code},
"grant_type":{"authorization_code"},
"redirect_uri": {redirect_uri},
}
client = &http.Client{}
req, err = http.NewRequest("POST", urlToken, strings.NewReader(formData.Encode()))
if err != nil {
fmt.Println("error prepping req to /token")
fmt.Println(err)
}
req.SetBasicAuth(client_id, client_secret)
req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
resp, err = client.Do(req)
if err != nil {
fmt.Println("error posting data to /token")
fmt.Println(err)
}
err = json.NewDecoder(resp.Body).Decode(&body)
if err != nil {
fmt.Println("error decoding body from /token")
fmt.Println(err)
}
fmt.Println("access_token:", body["access_token"])
}
Hello @phi1ipp ,
Using your code I only changed to the Org authorization server (without “default”) to got a token for the scopes okta.users.read/okta.groups.read and everything worked fine.
I appreciated your help. Really impressive the Okta Team support.
Thank you! I was a long time trying to solve this challenge and your help was amazing.
system
Closed
September 7, 2021, 6:58pm
7
This topic was automatically closed 24 hours after the last reply. New replies are no longer allowed.