Skip to content

Commit

Permalink
Merge pull request #329 from okta/allow_device_context_setter_at_trx_…
Browse files Browse the repository at this point in the history
…begin

Allow setting RequestContext per interaction and remove hardcoding of User Agent
  • Loading branch information
arvindkrishnakumar-okta authored May 4, 2022
2 parents 1bdbd58 + 1ae2d88 commit 48a5f44
Show file tree
Hide file tree
Showing 18 changed files with 314 additions and 136 deletions.
16 changes: 16 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,6 +105,22 @@ Begin Transaction with Activation token:
AuthenticationResponse beginResponse = idxAuthenticationWrapper.begin("activation-token");
```

Begin Transaction with Request Context:

`RequestContext` provides a way for clients to supply supplemental information such as
device token, user agent and IP address to Okta as request headers.

Note that Device Token and IP Address headers are relevant for Confidential clients only.

```java
RequestContext requestContext = new RequestContext();
requestContext.setDeviceToken("26q43Ak9Eh04p7H6Nnx0m69JqYOrfVBY"); // random string of length 32 characters
requestContext.setUserAgent("Chrome/46.0.2490.86"); // client user agent
requestContext.setIpAddress("23.235.46.133"); // client IP address

AuthenticationResponse beginResponse = idxAuthenticationWrapper.begin(requestContext);
```

Authenticate User:

```java
Expand Down
6 changes: 6 additions & 0 deletions api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -94,6 +94,12 @@
<profile>
<id>ci</id>
<build>
<resources>
<resource>
<directory>src/main/resources</directory>
<filtering>true</filtering>
</resource>
</resources>
<plugins>
<plugin>
<groupId>com.github.siom79.japicmp</groupId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import com.okta.idx.sdk.api.model.AuthenticationStatus;
import com.okta.idx.sdk.api.model.CurrentAuthenticatorEnrollment;
import com.okta.idx.sdk.api.model.CurrentAuthenticatorEnrollmentValue;
import com.okta.idx.sdk.api.model.RequestContext;
import com.okta.idx.sdk.api.model.EmailTokenType;
import com.okta.idx.sdk.api.model.FormValue;
import com.okta.idx.sdk.api.model.IDXClientContext;
Expand Down Expand Up @@ -56,28 +57,30 @@ final class AuthenticationTransaction {
private final IDXClientContext clientContext;
private final IDXResponse idxResponse;

static AuthenticationTransaction create(IDXClient client) throws ProcessingException {
return create(client, null, null, null);
}

static AuthenticationTransaction create(IDXClient client, RequestContext requestContext) throws ProcessingException {
return create(client, null, null, requestContext);
}

AuthenticationTransaction(IDXClient client, IDXClientContext clientContext, IDXResponse idxResponse) {
this.client = client;
this.clientContext = clientContext;
this.idxResponse = idxResponse;
}

static AuthenticationTransaction create(IDXClient client) throws ProcessingException {
return create(client, null, null);
}

static AuthenticationTransaction create(IDXClient client, String token, EmailTokenType tokenType) throws ProcessingException {
IDXClientContext idxClientContext;

if (token == null) {
idxClientContext = client.interact();
} else {
Assert.notNull(tokenType, "token type may not be null");
idxClientContext = client.interact(token, tokenType);
}
static AuthenticationTransaction create(IDXClient client,
String token,
EmailTokenType tokenType,
RequestContext requestContext) throws ProcessingException {

IDXClientContext idxClientContext = client.interact(token, tokenType, requestContext);
Assert.notNull(idxClientContext, "IDX client context may not be null");

IDXResponse introspectResponse = client.introspect(idxClientContext);

String stateHandle = introspectResponse.getStateHandle();
Assert.hasText(stateHandle, "State handle may not be null");

Expand Down
37 changes: 30 additions & 7 deletions api/src/main/java/com/okta/idx/sdk/api/client/BaseIDXClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -30,11 +30,13 @@
import com.okta.commons.http.Response;
import com.okta.commons.http.authc.DisabledAuthenticator;
import com.okta.commons.http.config.HttpClientConfiguration;
import com.okta.commons.lang.ApplicationInfo;
import com.okta.commons.lang.Assert;
import com.okta.commons.lang.Classes;
import com.okta.commons.lang.Strings;
import com.okta.idx.sdk.api.config.ClientConfiguration;
import com.okta.idx.sdk.api.exception.ProcessingException;
import com.okta.idx.sdk.api.model.RequestContext;
import com.okta.idx.sdk.api.model.EmailTokenType;
import com.okta.idx.sdk.api.model.FormValue;
import com.okta.idx.sdk.api.model.IDXClientContext;
Expand Down Expand Up @@ -67,8 +69,6 @@

final class BaseIDXClient implements IDXClient {

private static final String USER_AGENT_HEADER_VALUE = "okta-idx-java/2.0.0";

private final ClientConfiguration clientConfiguration;

private final ObjectMapper objectMapper;
Expand Down Expand Up @@ -97,11 +97,11 @@ public BaseIDXClient(ClientConfiguration clientConfiguration, RequestExecutor re

@Override
public IDXClientContext interact() throws ProcessingException {
return interact(null, null);
return interact(null, null, null);
}

@Override
public IDXClientContext interact(String token, EmailTokenType tokenType) throws ProcessingException {
public IDXClientContext interact(String token, EmailTokenType tokenType, RequestContext requestContext) throws ProcessingException {

InteractResponse interactResponse;
String codeVerifier, codeChallenge, state;
Expand Down Expand Up @@ -129,8 +129,26 @@ public IDXClientContext interact(String token, EmailTokenType tokenType) throws
}

HttpHeaders httpHeaders = getHttpHeaders(true);
if (clientConfiguration.getDeviceContext() != null) {
httpHeaders.setAll(clientConfiguration.getDeviceContext().getAll());

// include additional headers (for interact endpoint only), if present in request context.
if (requestContext != null) {
if (Strings.hasText(requestContext.getUserAgent())) {
httpHeaders.set(RequestContext.X_OKTA_USER_AGENT_EXTENDED,
requestContext.getUserAgent());
}

// set 'X-Forwarded-For' & 'X-Device-Token' headers for confidential clients only,
// these headers will be ignored for non-confidential clients.
if (Strings.hasText(clientConfiguration.getClientSecret())) {
if (Strings.hasText(requestContext.getDeviceToken())) {
httpHeaders.set(RequestContext.X_DEVICE_TOKEN,
requestContext.getDeviceToken());
}
if (Strings.hasText(requestContext.getIpAddress())) {
httpHeaders.set(RequestContext.X_FORWARDED_FOR,
requestContext.getIpAddress());
}
}
}

Request request = new DefaultRequest(
Expand Down Expand Up @@ -623,7 +641,12 @@ private HttpHeaders getHttpHeaders(boolean isOAuth2Endpoint) {
httpHeaders.add("Accept", "application/ion+json; okta-version=1.0.0");
}

httpHeaders.add(HttpHeaders.USER_AGENT, USER_AGENT_HEADER_VALUE);
String userAgentValue = ApplicationInfo.get().entrySet().stream()
.map(entry -> entry.getKey() + "/" + entry.getValue())
.collect(Collectors.joining(" "));

// value would look like (for e.g.): okta-idx-java/3.0.0-SNAPSHOT java/1.8.0_322 Mac OS X/12.3.1
httpHeaders.add(HttpHeaders.USER_AGENT, userAgentValue);
return httpHeaders;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,6 @@
import com.okta.idx.sdk.api.io.DefaultResourceFactory;
import com.okta.idx.sdk.api.io.Resource;
import com.okta.idx.sdk.api.io.ResourceFactory;
import com.okta.idx.sdk.api.model.DeviceContext;

import static com.okta.idx.sdk.api.util.Constants.*;

Expand Down Expand Up @@ -162,12 +161,6 @@ public IDXClientBuilder setRedirectUri(String redirectUri) {
return this;
}

@Override
protected IDXClientBuilder setDeviceContext(DeviceContext deviceContext) {
this.clientConfig.setDeviceContext(deviceContext);
return this;
}

@Override
public IDXClient build() {
this.validate();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
import com.okta.idx.sdk.api.model.AuthenticatorEnrollment;
import com.okta.idx.sdk.api.model.AuthenticatorEnrollments;
import com.okta.idx.sdk.api.model.Credentials;
import com.okta.idx.sdk.api.model.DeviceContext;
import com.okta.idx.sdk.api.model.RequestContext;
import com.okta.idx.sdk.api.model.EmailTokenType;
import com.okta.idx.sdk.api.model.FormValue;
import com.okta.idx.sdk.api.model.IDXClientContext;
Expand Down Expand Up @@ -99,28 +99,12 @@ public IDXAuthenticationWrapper() {
*/
public IDXAuthenticationWrapper(String issuer, String clientId, String clientSecret,
Set<String> scopes, String redirectUri) {
this(issuer, clientId, clientSecret, scopes, redirectUri, null);
}

/**
* Creates {@link IDXAuthenticationWrapper} instance.
*
* @param issuer the issuer url
* @param clientId the client id
* @param clientSecret the client secret
* @param scopes the set of scopes
* @param redirectUri the redirect uri
* @param deviceContext the device context information
*/
public IDXAuthenticationWrapper(String issuer, String clientId, String clientSecret,
Set<String> scopes, String redirectUri, DeviceContext deviceContext) {
this.client = Clients.builder()
.setIssuer(issuer)
.setClientId(clientId)
.setClientSecret(clientSecret)
.setScopes(scopes)
.setRedirectUri(redirectUri)
.setDeviceContext(deviceContext)
.build();
}

Expand Down Expand Up @@ -809,12 +793,22 @@ public boolean isSkipAuthenticatorPresent(ProceedContext proceedContext) {
}

/**
* Begin flow without any recovery or activation token.
* Begin flow without any recovery or activation token or request context.
* @return authentication response
*/
public AuthenticationResponse begin() {
return begin(null);
}

/**
* Begin flow with {@link RequestContext} reference.
*
* @param requestContext the RequestContext
* @return authentication response
*/
public AuthenticationResponse begin(RequestContext requestContext) {
try {
return AuthenticationTransaction.create(client).asAuthenticationResponse();
return AuthenticationTransaction.create(client, null, null, requestContext).asAuthenticationResponse();
} catch (ProcessingException e) {
return handleProcessingException(e);
} catch (IllegalArgumentException e) {
Expand All @@ -824,12 +818,15 @@ public AuthenticationResponse begin() {

/**
* Begin password recovery flow with a recovery token.
*
* @param token recovery token
* @param requestContext request context (optional)
* @return authentication response
*/
public AuthenticationResponse beginPasswordRecovery(String token) {
public AuthenticationResponse beginPasswordRecovery(String token, RequestContext requestContext) {
try {
return AuthenticationTransaction.create(client, token, EmailTokenType.RECOVERY_TOKEN).asAuthenticationResponse();
return AuthenticationTransaction.create(client, token, EmailTokenType.RECOVERY_TOKEN, requestContext)
.asAuthenticationResponse();
} catch (ProcessingException e) {
return handleProcessingException(e);
} catch (IllegalArgumentException e) {
Expand All @@ -839,12 +836,15 @@ public AuthenticationResponse beginPasswordRecovery(String token) {

/**
* Begin password recovery flow with an activation token.
*
* @param token activation token
* @param requestContext request context (optional)
* @return authentication response
*/
public AuthenticationResponse beginUserActivation(String token) {
public AuthenticationResponse beginUserActivation(String token, RequestContext requestContext) {
try {
return AuthenticationTransaction.create(client, token, EmailTokenType.ACTIVATION_TOKEN).asAuthenticationResponse();
return AuthenticationTransaction.create(client, token, EmailTokenType.ACTIVATION_TOKEN, requestContext)
.asAuthenticationResponse();
} catch (ProcessingException e) {
return handleProcessingException(e);
} catch (IllegalArgumentException e) {
Expand All @@ -854,6 +854,7 @@ public AuthenticationResponse beginUserActivation(String token) {

/**
* Exchange interaction code for token.
*
* @param proceedContext proceed context
* @param interactionCode interaction code
* @return authentication response
Expand Down
3 changes: 2 additions & 1 deletion api/src/main/java/com/okta/idx/sdk/api/client/IDXClient.java
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@

import com.okta.commons.http.Response;
import com.okta.idx.sdk.api.exception.ProcessingException;
import com.okta.idx.sdk.api.model.RequestContext;
import com.okta.idx.sdk.api.model.EmailTokenType;
import com.okta.idx.sdk.api.model.IDXClientContext;
import com.okta.idx.sdk.api.request.AnswerChallengeRequest;
Expand All @@ -37,7 +38,7 @@ public interface IDXClient {

IDXClientContext interact() throws ProcessingException;

IDXClientContext interact(String token, EmailTokenType tokenType) throws ProcessingException;
IDXClientContext interact(String token, EmailTokenType tokenType, RequestContext requestContext) throws ProcessingException;

IDXResponse introspect(IDXClientContext idxClientContext) throws ProcessingException;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,6 @@
*/
package com.okta.idx.sdk.api.client;

import com.okta.idx.sdk.api.model.DeviceContext;

import java.util.Set;

abstract class IDXClientBuilder {
Expand All @@ -31,7 +29,5 @@ abstract class IDXClientBuilder {

protected abstract IDXClientBuilder setRedirectUri(String redirectUri);

protected abstract IDXClientBuilder setDeviceContext(DeviceContext deviceContext);

protected abstract IDXClient build();
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,6 @@
import com.okta.commons.http.authc.DisabledAuthenticator;
import com.okta.commons.http.authc.RequestAuthenticator;
import com.okta.commons.http.config.HttpClientConfiguration;
import com.okta.idx.sdk.api.model.DeviceContext;

import java.net.MalformedURLException;
import java.net.URL;
Expand All @@ -42,7 +41,6 @@ public class ClientConfiguration extends HttpClientConfiguration {
private String clientSecret;
private Set<String> scopes = new HashSet<>();
private String redirectUri;
private DeviceContext deviceContext;

public String getIssuer() {
return issuer;
Expand Down Expand Up @@ -84,14 +82,6 @@ public void setRedirectUri(String redirectUri) {
this.redirectUri = redirectUri;
}

public DeviceContext getDeviceContext() {
return deviceContext;
}

public void setDeviceContext(DeviceContext deviceContext) {
this.deviceContext = deviceContext;
}

@Override
public RequestAuthenticator getRequestAuthenticator() {
return new DisabledAuthenticator();
Expand Down
Loading

0 comments on commit 48a5f44

Please sign in to comment.