Intercept and modify REST calls
The Ping SDKs support modification of REST calls before they are sent.
For example, you can customize:
-
Request parameters
-
Headers
-
Cookies
-
Request URLs
-
Request methods
-
The request body and post data
Request interceptors
Each SDK provides an interface that you can use to customize requests:
-
Android
-
iOS
-
JavaScript
public interface FRRequestInterceptor<Action> {
@NonNull Request intercept(Request request, Action action);
}
public class Action {
private String type;
private JSONObject payload;
}
public protocol RequestInterceptor {
func intercept(request: Request, action: Action) -> Request
}
public struct Action {
public let type: String
public let payload: [String: Any]?
}
type RequestMiddleware = (req: RequestObj, action: Action, next: () => RequestObj) => void;
interface RequestObj {
url: URL;
init: RequestInit;
}
interface Action {
type: string;
payload?: any; // optional data
}
Request interceptors have two inputs:
The Request
object
Represents the original request, and has information about the body, method type, parameters, and more.
-
Android
-
iOS
-
JavaScript
public class Request {
public URL url();
public Iterator<Pair<String, String>> headers();
public String header(String name);
public List<String> headers(String name);
public String method();
public Object tag();
public Body body();
public Builder newBuilder();
}
// Use Build to build upon on existing Request
public class Builder {
public Builder url(URL url);
public Builder url(String url);
public Builder header(String name, String value);
public Builder addHeader(String name, String value);
public Builder removeHeader(String name);
public Builder get();
public Builder put(Body body);
public Builder post(Body body);
public Builder delete(Body body);
public Builder delete();
public Builder patch(Body body);
public Request build();
}
public struct Request {
// Properties
public let url: String
public let method: HTTPMethod
private(set) public var headers: [String: String]
public let bodyParams: [String: Any]
public let urlParams: [String: String]
public let responseType: ContentType
public let requestType: ContentType
public let timeoutInterval: Double
public enum ContentType: String {
case plainText = "text/plain"
case json = "application/json"
case urlEncoded = "application/x-www-form-urlencoded"
}
public enum HTTPMethod: String {
case GET = "GET"
case PUT = "PUT"
case POST = "POST"
case DELETE = "DELETE"
}
public func build() -> URLRequest?
}
Refer to the native JavaScript Request
object in the MDN Web Docs.
The Action
object
Represents the type of operation the request performs:
Action | Description |
---|---|
|
Initial call to an authentication tree |
|
Proceed through an authentication tree flow |
|
Obtain authorization token from PingAM |
|
Exchange authorization code for an access token |
|
Refresh an access token |
|
Revoke a refresh or access token |
|
Log out a session |
|
Obtain information from the |
|
Register a push device with PingAM;
for example, a call to |
|
Authenticate using push;
for example, a call to |
The
|
The outcome of applying a request interceptor is the entire modified request object, ready to either be sent to PingAM, or to have additional request interceptors applied.
Examples
This section covers how to develop request interceptors, referred to as "middleware" in the Ping SDK for JavaScript, and apply them to outbound requests from your applications.
Ping SDK for Android
Query parameters and headers
The example sets the ForceAuth
query parameter to true
, and adds an Accept-Language
header with a value of en-GB
on all outgoing requests of the START_AUTHENTICATE
type:
public class QueryParamsAndHeaderRequestInterceptor implements FRRequestInterceptor<Action> {
@NonNull
@Override
public Request intercept(@NonNull Request request, Action tag) {
if (tag.getType().equals(START_AUTHENTICATE)) {
return request.newBuilder()
// Add query parameter:
.url(Uri.parse(request.url().toString())
.buildUpon()
.appendQueryParameter("ForceAuth", "true").toString())
// Add additional header:
.addHeader("Accept-Language", "en-GB")
// Construct the updated request:
.build();
}
return request;
}
}
To register the request interceptor, use the RequestInterceptorRegistry.getInstance().register()
method:
RequestInterceptorRegistry.getInstance().register(new QueryParamsAndHeaderRequestInterceptor())
Any calls the app makes to initiate authentication now have the query parameter ForceAuth=true
appended, and include an accept-language: en-GB
header added.
Cookies
The example adds a custom cookie to outgoing requests:
public class CustomCookieInterceptor implements FRRequestInterceptor<Action>, CookieInterceptor {
@NonNull
@Override
public Request intercept(@NonNull Request request) {
return request;
}
@NonNull
@Override
public Request intercept(@NonNull Request request, Action tag) {
return request;
}
@NonNull
@Override
public List<Cookie> intercept(@NonNull List<Cookie> cookies) {
List<Cookie> newCookies = new ArrayList<>();
newCookies.addAll(cookies);
newCookies.add(
new Cookie.Builder()
.domain("example.com")
.name("member").value("gold")
.httpOnly().secure().build()
);
return newCookies;
}
}
You can register multiple request interceptors as follows:
|
Ping SDK for iOS
Query parameters and headers
The example sets the ForceAuth
query parameter to true
, and adds an Accept-Language
header with a value of en-GB
on all outgoing requests of the AUTHENTICATE
or START_AUTHENTICATE
type:
class QueryParamsAndHeaderRequestInterceptor: RequestInterceptor {
func intercept(request: Request, action: Action) -> Request {
if action.type == "START_AUTHENTICATE" || action.type == "AUTHENTICATE" {
// Add query parameter:
var urlParams = request.urlParams
urlParams["ForceAuth"] = "true"
// Add additional header:
var headers = request.headers
headers["Accept-Language"] = "en-GB"
// Construct the updated request:
let newRequest = Request(
url: request.url,
method: request.method,
headers: headers,
bodyParams: request.bodyParams,
urlParams: urlParams,
requestType: request.requestType,
responseType: request.responseType,
timeoutInterval: request.timeoutInterval
)
return newRequest
}
else {
return request
}
}
}
To register the request interceptor, use the registerInterceptors()
method:
FRRequestInterceptorRegistry.shared.registerInterceptors(
interceptors: [
QueryParamsAndHeaderRequestInterceptor()
]
)
Any calls the app makes to initiate authentication now have the query parameter ForceAuth=true
appended, and include an accept-language: en-GB
header added.
Cookies
The example adds a custom cookie to outgoing requests:
class CookieInterceptor: RequestInterceptor {
func intercept(request: Request, action: Action) -> Request {
if action.type == "START_AUTHENTICATE" || action.type == "AUTHENTICATE" {
var headers = request.headers
headers["Cookie"] = "member=gold; level=2"
let newRequest = Request(
url: request.url,
method: request.method,
headers: headers,
bodyParams: request.bodyParams,
urlParams: request.urlParams,
requestType: request.requestType,
responseType: request.responseType,
timeoutInterval: request.timeoutInterval)
return newRequest
}
else {
return request
}
}
}
You can register multiple request interceptors as follows:
|
Ping SDK for JavaScript
The example has two middleware configurations.
One sets the ForceAuth
query parameter to true
, the other adds an Accept-Language
header with a value of en-GB
on all outgoing requests of the START_AUTHENTICATE
type:
const forceAuthMiddleware = (
req: RequestObj,
action: Action,
next: () => RequestObj
): void => {
switch (action.type) {
case 'START_AUTHENTICATE':
req.url.searchParams.set('ForceAuth', 'true');
break;
}
next();
};
const addHeadersMiddleware = (
req: RequestObj,
action: Action,
next: () => RequestObj
): void => {
switch (action.type) {
case 'START_AUTHENTICATE':
const headers = req.init.headers as Headers;
headers.append('Accept-Language', 'en-GB');
break;
}
next();
};
Apply the middleware in the config
:
Config.set({
clientId: 'sdkPublicClient',
middleware: [
forceAuthMiddleware,
addHeadersMiddleware
],
redirectUri: 'https://localhost:8443/callback.html',
realmPath: 'alpha',
scope: 'openid profile email address',
serverConfig: {
baseUrl: 'https://openam-forgerock-sdks.forgeblocks.com/am',
timeout: 30000
},
tree: 'UsernamePassword'
});
Any calls the app makes to start authentication now have the query parameter and header added.
You can only modify headers in certain types of request. For example |