I am writing unit tests for our API. My UI is a Angular SPA so we are using the Implicit flow. We have a WebAPI backend.
I am writing integration tests which run in our continuous delivery pipeline.
How can I call the OKTA api rest endpoints with HttpClient and a testing userid and password so that I can retrieve a valid id_token or access_token to my API for integration testing?
You can directly get the access_token from /token endpoint by passing the client credentials
You only need to call the /token Okta API
Cons -
You will need to store the client_id & client_secret securely (Assuming you are using a CI that can store encrypted variables, you should be good)
You will need to create a new application in your okta org
Your resource server backend must be configured to accept an additional âtest client_idâ (Not entirely sure how to configure this)
If you want to use the implicit flow, you will need to follow redirects from the /authorize endpoint and retrieve the access_token from the redirect URI (either as query parameter or hash fragment)
Call the /authorize endpoint by passing this sessionToken, which will return access_token in the redirect URI which can be then be extracted in your HttpClient. You can use the response_mode as fragment to the authorize endpoint which will then return the access_token as a hash fragment or by default itâs returned as a URL query param.
Thank you both, it worked! Here is the c# version for those that need it:
public class OktaRequests
{
public static async Task<Tokens> GetOktaToken()
{
var domain = "your okta domain";
var oktaAuthorizationServer = "your okta auth server (usually default)";
var clientId = "your okta application client id";
var redirectUrl = "your call back that you have set in OKTA";
var redirectUrlEncoded = System.Net.WebUtility.UrlEncode(redirectUrl);
var responseType = System.Net.WebUtility.UrlEncode("id_token token");
var state = "testing";
var nonce = "testing nonce";
var scope = System.Net.WebUtility.UrlEncode("openid email profile");
var authnUri = $"{domain}/api/v1/authn";
var username = "your testing userid";
var password = "your testing password";
dynamic bodyOfRequest = new
{
username,
password,
options = new
{
multiOptionalFactorEnroll = true,
warnBeforePasswordExpired = true
}
};
var body = JsonConvert.SerializeObject(bodyOfRequest);
var stringContent = new StringContent(body, Encoding.UTF8, "application/json");
string sessionToken;
HttpClientHandler httpClientHandler = new HttpClientHandler();
httpClientHandler.AllowAutoRedirect = false;
using (var httpClient = new HttpClient(httpClientHandler))
{
httpClient.DefaultRequestHeaders
.Accept
.Add(new MediaTypeWithQualityHeaderValue("application/json"));
HttpResponseMessage authnResponse = await httpClient.PostAsync(authnUri, stringContent);
if (authnResponse.IsSuccessStatusCode)
{
var authnResponseContent = await authnResponse.Content.ReadAsStringAsync();
dynamic authnObject = JsonConvert.DeserializeObject(authnResponseContent);
sessionToken = authnObject.sessionToken;
var authorizeUri = $"{domain}/oauth2/{oktaAuthorizationServer}/v1/authorize?client_id={clientId}&redirect_uri={redirectUrlEncoded}&response_type={responseType}&sessionToken={sessionToken}&state={state}&nonce={nonce}&scope={scope}";
HttpResponseMessage authorizeResponse = await httpClient.GetAsync(authorizeUri);
var statusCode = (int)authorizeResponse.StatusCode;
if(statusCode == System.Net.HttpStatusCode.Found)
{
var redirectUri = authorizeResponse.Headers.Location;
var queryDictionary = HttpUtility.ParseQueryString(redirectUri.AbsoluteUri);
var idTokenKey = $"{oktaAuthRequestInformation.RedirectUrl}#id_token";
return new Tokens
{
idToken = queryDictionary[idTokenKey],
accessToken = queryDictionary["access_token"]
};
}
}
}
throw new Exception("Something went wrong");
}
}
Based on the two posts I made a similar request in Java. Thanks to the input from theses posts, I managed to create something that works with Spring Boot.
I tried this out and it worked, what I want to do next is saving the token in database and checking if itâs valid so that I do not need to do a new request for every test.
I use this token for my in testing in Spring boot. The Junit testâs will create a in memory database and start the application and then I will call the endpoint to get a access token so that I can send it to my secured endpoints and confirm that everything is working properly.
public String createToken() {
final String DOMAIN = "https://{oktaDomain}.com/";
final String CLIENT_ID = "{clientId}";
final String REDIRECT_URI = "{redirectUri}";
final String USERNAME = "username@mail.com";
final String PASSWORD = "password";
final EntityLogin entityLogin = new EntityLogin(USERNAME, PASSWORD);
final HttpHeaders headers = new HttpHeaders();
headers.setContentType(MediaType.APPLICATION_JSON);
final WebTarget authentication = ClientBuilder.newClient().target(DOMAIN + "/api/v1/authn");
final Response response = authentication
.request(String.valueOf(MediaType.APPLICATION_JSON))
.post(Entity.json(entityLogin));
final String[] responseFromAuthentication = response.readEntity(String.class).split(":");
final String sessionToken = responseFromAuthentication[5].subSequence(1, 56).toString();
final WebTarget authorization = ClientBuilder.newClient()
.target(DOMAIN + "/oauth2/default/v1/authorize")
.queryParam("response_type", "token")
.queryParam("scope", "openid")
.queryParam("state", "TEST")
.queryParam("nonce", "TEST")
.queryParam("client_id", CLIENT_ID)
.queryParam("redirect_uri", REDIRECT_URI)
.queryParam("sessionToken", sessionToken);
final String accessToken = authorization.request().get().getLocation().getFragment().substring(13);
return accessToken.substring(0, accessToken.indexOf("&"));
}
private class EntityLogin {
private String username;
private String password;
}
Hi, @tom@vijet@lboyette
We are looking for something similar, except that we have a Web Application where we want to pass the session token to the /authorize call for each user.
Here are the specifics and the problem we are running into:
We have a Web Application portal into which users login(not using oktaâs login page, just an application login page).
We have created an App Integration in Okta which is having Application Type as âWeb Applicationâ.
We capture the user email and password from the front end and pass it to server side and are making an Okta API call to the Authentication API and receiving a sessiontoken back.
Till here things are good.
Now the new requirement is to : Get user specific access token in the server side code and pass it as a JWT token to another API.
What we tried doing:
Make a call to the /authorize endpoint, passing the session token(and other relevant parameters)
Issue we are running into:
It is showing the Okta Login page again and if we set prompt=none, then it says user is not signed in.
Since the user was already authenticated in the previous step using authentication API, we donât want them to have to put in credentials again.
Is there a way to avoid that?
Note 1:
Our understanding is we cannot use the Client Credentials Flow in our case, because then the access token received back is not for the user logged in, rather for the application, please correct if this is a wrong assumption.
Note 2:
We did find an option where we set the Application Type as âNative Applicationâ and use the Resource Owner Password grant type, so in that approach, for /authorize endpoint call we need to pass the user email and password.
But we didnât want to pass those if possible for security reasons and also we didnât want to switch from the existing âWeb Applicationâ in Okta to âNative Applicationâ. (The reason we had to choose âNative Applicationâ is because the Resource owner password grant type is not an option for âWeb Applicationâ.)
Any suggestions or directions on this would be great!