diff --git a/docs/source/forwarded-request.rst b/docs/source/forwarded-request.rst
index b05164f05c..30db8ff6f7 100644
--- a/docs/source/forwarded-request.rst
+++ b/docs/source/forwarded-request.rst
@@ -3,12 +3,13 @@
Forwarded Request
=================
-If a user has authenticated, either by using a :ref:`login form ` or via :ref:`Request Authentication `,
-the user account's data will be forwarded to any backend origin servers behind the gateway in a request header.
+If a user has authenticated, either by using a :ref:`login form ` or via
+:ref:`Request Authentication `, the user account data will be forwarded to any backend
+origin servers behind the gateway in a request header.
-The origin server(s) may inspect this request header to discover information about the account. This information can
+The origin server(s) may inspect this request header to discover information about the user. This information can
be used to customize views, send emails, update user-specific data, or practically anything else you can think of that
-might be user specific.
+might be user-specific.
This page covers how to customize everything related to this header and its contents.
@@ -19,9 +20,9 @@ This page covers how to customize everything related to this header and its cont
Forwarded Account Header Name
-----------------------------
-If a user ``Account`` is associated with a request that will be forwarded to an origin server, the account will be
+If a user account is associated with a request that will be forwarded to an origin server, the account will be
converted to a String and added to the forwarded request as an HTTP request header. The default header name is
-``X-Forwarded-Account``.
+``X-Forwarded-User``, a de-facto HTTP header for forwarding user account information.
If you want to specify a different HTTP header name, set the ``stormpath.zuul.account.header.name`` configuration property:
@@ -31,7 +32,7 @@ If you want to specify a different HTTP header name, set the ``stormpath.zuul.ac
zuul:
account:
header:
- name: X-Forwarded-Account
+ name: X-Forwarded-User
.. caution::
@@ -39,17 +40,17 @@ If you want to specify a different HTTP header name, set the ``stormpath.zuul.ac
.. note::
- If there is no account associated with the request, the header will not be present in the forwarded request at all.
+ If there is no user account associated with the request, the header will not be present in the forwarded request at all.
Forwarded Account Header Value
------------------------------
-If an ``Account`` is associated with the request, the forwarded account header value will be a String that represents the
+If a user ``Account`` is associated with the request, the forwarded account header value will be a String that represents the
account.
-You can customize this String value to be anything you like - such as a simple single value like the Account's username
-or email, or the entire Account as a JSON document, or even a cryptographically-safe
-:ref:`JSON Web Token (JWT) ` that represents the Account information you choose.
+You can customize this String value to be anything you like - such as a simple single value like the account's username
+or email, or the entire account as a JSON document, or even a cryptographically-safe
+:ref:`JSON Web Token (JWT) ` that represents the account information you choose.
By default, a digitally-signed account :ref:`JWT ` will be used as the header value.
When an origin server reads the forwarded header value, the origin server can verify the JWT signature. This allows
@@ -72,7 +73,7 @@ Single Account Field
^^^^^^^^^^^^^^^^^^^^
If you do not want or need the security guarantees of a JWT and want your header value to be a single string value,
-like the account's username or email, you can set the following configuration:
+such as the account's username or email, you can set the following configuration:
.. code-block:: yaml
@@ -98,7 +99,7 @@ the origin server(s) would look like this:
.. code-block:: properties
- x-forwarded-account: tk421
+ x-forwarded-user: tk421
A similar example using the account email instead is shown in the :ref:`field ` section.
@@ -426,7 +427,7 @@ the header sent to the origin server(s) would look like this:
.. code-block:: properties
- x-forwarded-account: tk421@galacticempire.com
+ x-forwarded-user: tk421@galacticempire.com
.. _object conversion fields:
@@ -1023,7 +1024,7 @@ The ``valueClaim`` config properties allow you to control how the :ref:`Account
represented inside the JWT.
By default, the :ref:`Account JSON ` is represented under a single JWT claim named
-``account``. This results in JWT claims that look something like this:
+``user``. This results in JWT claims that look something like this:
.. code-block:: javascript
:emphasize-lines: 5-12
@@ -1032,7 +1033,7 @@ By default, the :ref:`Account JSON ` is represented unde
"iat": 1482972605,
"iss": "my gateway",
"aud": "my origin server",
- "account": {
+ "user": {
"username": "tk421",
"email": "tk421@galacticempire.com",
"givenName": "TK421",
@@ -1044,9 +1045,9 @@ By default, the :ref:`Account JSON ` is represented unde
}
-As you can see, the account JSON is reflected as a single ``account`` claim, and the entire account can be
-retrieved by a single lookup of that claim. This helps keep your account information 'clean' and separate from other
-JWT claims like ``iat``, ``iss``, ``aud``, etc.
+As you can see, the user account JSON is reflected as a single ``user`` claim, and the entire user account can be
+retrieved by a single lookup of that claim. This helps keep your user account information 'clean' and separate from
+other JWT claims like ``iat``, ``iss``, ``aud``, etc.
If you prefer, you can :ref:`change the claim name ` or
:ref:`not use a claim at all `
@@ -1054,14 +1055,14 @@ via the respective nested ``name`` and ``enabled`` properties.
.. tip::
- For you JWT experts out there, you might want to know why we didn't represent the account with the
+ For you JWT experts out there, you might want to know why we didn't represent the user account with the
`JWT sub claim `_ . The ``sub`` claim is the RFC-standard claim
- that defines the target identity of the JWT, and the account is the identity we care about, right? So why didn't we
- just use the default ``sub`` claim instead of ``account``?
+ that defines the target identity of the JWT, and the user account is the identity we care about, right? So why
+ didn't we just use the default ``sub`` claim instead of ``user``?
The reason is that the JWT RFC (`RFC 7519 `_) says that the value of the ``sub``
claim must be a ``StringOrURI`` data type value, as defined in
- `RFC 7519 section 2 (Terminology) `_. The Account JSON is a full
+ `RFC 7519 section 2 (Terminology) `_. The user account JSON is a full
JSON object structure, which is neither a String nor a URI as required by the RFC. So, we choose a different
claim name to avoid any parsing/validation errors that JWT libraries might enforce for that claim, and all is well.
@@ -1071,7 +1072,7 @@ via the respective nested ``name`` and ``enabled`` properties.
``enabled``
"""""""""""
-The :ref:`Account JSON ` is nested in the JWT claims as single claim named ``account`` by
+The :ref:`Account JSON ` is nested in the JWT claims as single claim named ``user`` by
default.
If you don't want to use a specific value claim at all, and instead prefer to have the account properties mixed
@@ -1089,7 +1090,7 @@ entirely by setting ``stormpath.zuul.account.header.jwt.valueClaim.enabled`` to
enabled: false
-After setting this property to ``false``, all account JSON name/value pairs are added directly to the JWT claims,
+After setting this property to ``false``, all user account JSON name/value pairs are added directly to the JWT claims,
making each account property a claim itself. The account properties and any other JWT-related ones are all
intermixed and 'just claims' as far as the JWT is concerned. For example:
@@ -1114,7 +1115,7 @@ intermixed and 'just claims' as far as the JWT is concerned. For example:
``name``
""""""""
-The single value claim is named ``account`` by default. You can change this name if you prefer by setting the
+The single value claim is named ``user`` by default. You can change this name if you prefer by setting the
``stormpath.zuul.account.header.jwt.valueClaim.name`` config property. For example:
.. code-block:: yaml
@@ -1125,7 +1126,7 @@ The single value claim is named ``account`` by default. You can change this nam
header:
jwt:
valueClaim:
- name: user
+ name: userAccount
This would result in JWT claims that look something like this:
@@ -1136,7 +1137,7 @@ This would result in JWT claims that look something like this:
"iat": 1482972605,
"iss": "my gateway",
"aud": "my origin server",
- "user": {
+ "userAccount": {
"username": "tk421",
"email": "tk421@galacticempire.com",
"givenName": "TK421",
@@ -1173,21 +1174,61 @@ Custom Header Value
-------------------
Finally, if *none* of the above options are sufficient for you, don't worry, we still have you covered. You can still
-create any string you want as the header value with a little custom code. You have two easy options:
+create any string you want as the header value with a little custom code. You have three easy options:
+
+1. If you don't need access to the HttpServletRequest/Response pair and want to convert the Account to a
+ Map that will be automatically turned into JSON or a JWT for you, you can define your own
+ :ref:`account-to-map conversion function ` bean.
-1. If you don't need access to the HttpServletRequest/Response pair and just want to convert an Account
- object to a String, you can define your own
+2. If you don't need access to the HttpServletRequest/Response pair and want to do the full account to final
+ header String conversion logic yourself, you can define your own
:ref:`account-to-string conversion function ` bean.
-2. If you need access to the HttpServletRequest/Response during the account-to-string conversion process, you can
+3. If you need access to the HttpServletRequest/Response during the account-to-string conversion process, you can
define your own :ref:`stormpathForwardedAccountHeaderValueResolver` bean.
-In either case you will need to add the proper bean in your gateway Spring config.
+In any case you will need to add the proper bean in your gateway Spring config.
.. note::
- Remember that adding or changing either bean will probably require changes to your origin server(s) - the origin
- server(s) will need to understand how to read the different Account string value created by your conversion bean.
+ Remember that adding or changing any of these beans will probably require changes to your origin server(s) -
+ the origin server(s) will need to understand how to read the final Account string value created by your
+ conversion bean.
+
+
+.. _forwarded account map function:
+
+Account-to-Map Function
+^^^^^^^^^^^^^^^^^^^^^^^
+
+If you don't need access to the HttpServletRequest/Response pair, and you just want to be able to convert an ``Account``
+instance to a ``Map``, you can define your own ``stormpathForwardedAccountMapFunction`` bean:
+
+.. code-block:: java
+
+ @Bean
+ public Function stormpathForwardedAccountMapFunction() {
+ return new MyAccountToMapFunction(); //implement me
+ }
+
+
+This bean/method must be named ``stormpathForwardedAccountMapFunction`` and the bean must implement the
+``com.stormpath.sdk.lang.Function`` interface.
+
+When the gateway determines that there is an account to forward to an origin server, your custom function will be
+called with an ``Account`` instance and it will return a ``Map`` result.
+
+This resulting map will be
+converted to a JSON document automatically, and then potentially converted to a JWT depending on the value of the
+:ref:`stormpath.zuul.account.header.jwt.enabled ` property (which is enabled by
+default). If JWT is enabled, you can :ref:`customize the JWT as documented ` above.
+
+The final resulting JSON or JWT string will be the header value.
+
+.. note::
+
+ If the resulting Map is ``null`` or empty, the header will not be present in the forwarded request at all.
+
.. _forwarded account to string function:
@@ -1204,7 +1245,7 @@ instance to a String, you can define your own ``stormpathForwardedAccountStringF
return new MyAccountToStringFunction(); //implement me
}
-This bean/method must be named ``stormpathForwardedAccountStringFunction``. The bean must implement the
+This bean/method must be named ``stormpathForwardedAccountStringFunction`` and the bean must implement the
``com.stormpath.sdk.lang.Function`` interface.
When the gateway determines that there is an account to forward to an origin server, your custom function will be
diff --git a/extensions/spring/cloud/stormpath-zuul-spring-cloud-starter/src/main/java/com/stormpath/spring/cloud/zuul/autoconfigure/StormpathZuulAutoConfiguration.java b/extensions/spring/cloud/stormpath-zuul-spring-cloud-starter/src/main/java/com/stormpath/spring/cloud/zuul/autoconfigure/StormpathZuulAutoConfiguration.java
index 10e3bde545..6011f14fc4 100644
--- a/extensions/spring/cloud/stormpath-zuul-spring-cloud-starter/src/main/java/com/stormpath/spring/cloud/zuul/autoconfigure/StormpathZuulAutoConfiguration.java
+++ b/extensions/spring/cloud/stormpath-zuul-spring-cloud-starter/src/main/java/com/stormpath/spring/cloud/zuul/autoconfigure/StormpathZuulAutoConfiguration.java
@@ -242,7 +242,7 @@ protected static SignatureAlgorithm getAlgorithm(byte[] hmacSigningKeyBytes) {
}
if (!signatureAlgorithm.isHmac()) {
String msg = "Unable to use specified JWT signature algorithm '" + signatureAlgorithm + "' when " +
- "creating X-Forwarded-Account JWTs, as this algorithm is incompatible with the " +
+ "creating X-Forwarded-User JWTs, as this algorithm is incompatible with the " +
"fallback/default Stormpath Client ApiKey secret signing key. Defaulting to '" +
defaultSigAlg + "'. To avoid this message, either 1) do not specify a signature algorithm to " +
"let the framework choose an algorithm appropriate for the default signing key, or 2) define " +
@@ -291,6 +291,10 @@ public Function stormpathForwardedAccountStringFunction() {
public String apply(Account account) {
Object value = accountFunction.apply(account);
+ if (value == null || (value instanceof Map && Collections.isEmpty((Map)value))) {
+ return null;
+ }
+
if (value instanceof String) {
return (String)value;
}
diff --git a/extensions/spring/cloud/stormpath-zuul-spring-cloud-starter/src/main/java/com/stormpath/spring/cloud/zuul/config/StormpathZuulAccountHeaderConfig.java b/extensions/spring/cloud/stormpath-zuul-spring-cloud-starter/src/main/java/com/stormpath/spring/cloud/zuul/config/StormpathZuulAccountHeaderConfig.java
index 996ade4e39..c231534780 100644
--- a/extensions/spring/cloud/stormpath-zuul-spring-cloud-starter/src/main/java/com/stormpath/spring/cloud/zuul/config/StormpathZuulAccountHeaderConfig.java
+++ b/extensions/spring/cloud/stormpath-zuul-spring-cloud-starter/src/main/java/com/stormpath/spring/cloud/zuul/config/StormpathZuulAccountHeaderConfig.java
@@ -26,7 +26,7 @@
public class StormpathZuulAccountHeaderConfig {
@SuppressWarnings("WeakerAccess")
- public static final String DEFAULT_NAME = "X-Forwarded-Account";
+ public static final String DEFAULT_NAME = "X-Forwarded-User";
private String name;
diff --git a/extensions/spring/cloud/stormpath-zuul-spring-cloud-starter/src/main/java/com/stormpath/spring/cloud/zuul/config/ValueClaimConfig.java b/extensions/spring/cloud/stormpath-zuul-spring-cloud-starter/src/main/java/com/stormpath/spring/cloud/zuul/config/ValueClaimConfig.java
index 11c6f48584..7ae1018e5e 100644
--- a/extensions/spring/cloud/stormpath-zuul-spring-cloud-starter/src/main/java/com/stormpath/spring/cloud/zuul/config/ValueClaimConfig.java
+++ b/extensions/spring/cloud/stormpath-zuul-spring-cloud-starter/src/main/java/com/stormpath/spring/cloud/zuul/config/ValueClaimConfig.java
@@ -20,13 +20,15 @@
*/
public class ValueClaimConfig {
+ public static final String DEFAULT_CLAIM_NAME = "user";
+
private boolean enabled;
private String name;
public ValueClaimConfig() {
this.enabled = true;
- this.name = "account";
+ this.name = DEFAULT_CLAIM_NAME;
}
public boolean isEnabled() {
diff --git a/extensions/spring/cloud/stormpath-zuul-spring-cloud-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json b/extensions/spring/cloud/stormpath-zuul-spring-cloud-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json
index 5bb38afdab..4000fafb81 100644
--- a/extensions/spring/cloud/stormpath-zuul-spring-cloud-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json
+++ b/extensions/spring/cloud/stormpath-zuul-spring-cloud-starter/src/main/resources/META-INF/additional-spring-configuration-metadata.json
@@ -41,18 +41,18 @@
{
"name": "stormpath.zuul.account.header.name",
"type": "java.lang.Integer",
- "description": "The name of the HTTP header used in a forwarded request that contains a String representing the Account associated with the inbound request. If there is no account associated with the request, this header will not be set. Unless overridden, the default value is X-Forwarded-Account",
- "defaultValue": "X-Forwarded-Account"
+ "description": "The name of the HTTP header used in a forwarded request that contains a String representing the Account associated with the inbound request. If there is no account associated with the request, this header will not be set. Unless overridden, the default value is X-Forwarded-User",
+ "defaultValue": "X-Forwarded-User"
},
{
"name": "stormpath.zuul.account.header.value",
"type": "com.stormpath.sdk.convert.Conversion",
- "description": "The conversion rules to apply to any discovered Account to be forwarded. These rules produce an account String that will be used as the value of the X-Forwarded-Account header."
+ "description": "The conversion rules to apply to any discovered Account to be forwarded. These rules produce an account String that will be used as the value of the X-Forwarded-User header."
},
{
"name": "stormpath.zuul.account.header.jwt.enabled",
"type": "java.lang.Boolean",
- "description": "Whether or not the X-Forwarded-Account header value should be a JWT instead of a plaintext string. Unless overridden, the default value is true for extra security guarantees. A false value will result in a plaintext string header value.",
+ "description": "Whether or not the X-Forwarded-User header value should be a JWT instead of a plaintext string. Unless overridden, the default value is true for extra security guarantees. A false value will result in a plaintext string header value.",
"defaultValue": true
},
{
@@ -86,8 +86,8 @@
{
"name": "stormpath.zuul.account.header.jwt.valueClaim.name",
"type": "java.lang.String",
- "description": "The name of the claim within the forwarded account JWT claims map that represents the account object. Unless overridden, the default value is 'account'. This property is only evaluated if stormpath.zuul.account.header.jwt.valueClaim.enabled is equal to true.",
- "defaultValue": "account"
+ "description": "The name of the claim within the forwarded user JWT claims map that represents the user account object. Unless overridden, the default value is 'user'. This property is only evaluated if stormpath.zuul.account.header.jwt.valueClaim.enabled is equal to true.",
+ "defaultValue": "user"
},
{
"name": "stormpath.zuul.account.header.jwt.key.alg",
@@ -114,7 +114,7 @@
{
"name": "stormpath.zuul.account.header.jwt.key.kid",
"type": "java.lang.Boolean",
- "description": "Specifies the identifier of the signing key used to digitally sign the forwarded account JWT. This is useful because backend origin servers behind the gateway can inspect the X-Forwarded-Account header JWT, find this key id, and based on this id, look up the appropriate key that should be used to verify the JWT's digital signature. If you specify a signing key (and you should!) you would almost always want to set this property. If you do not specify a signing key or this property, the Stormpath Client API Key secret will be used as the HMAC signing key, and this property ('kid') will default to the Client API Key's HREF URL."
+ "description": "Specifies the identifier of the signing key used to digitally sign the forwarded account JWT. This is useful because backend origin servers behind the gateway can inspect the X-Forwarded-User header JWT, find this key id, and based on this id, look up the appropriate key that should be used to verify the JWT's digital signature. If you specify a signing key (and you should!) you would almost always want to set this property. If you do not specify a signing key or this property, the Stormpath Client API Key secret will be used as the HMAC signing key, and this property ('kid') will default to the Client API Key's HREF URL."
}
],
"hints": [
diff --git a/extensions/zuul/src/main/java/com/stormpath/zuul/account/ForwardedAccountHeaderFilter.java b/extensions/zuul/src/main/java/com/stormpath/zuul/account/ForwardedAccountHeaderFilter.java
index 179d6fd90a..5dee5aaf6a 100644
--- a/extensions/zuul/src/main/java/com/stormpath/zuul/account/ForwardedAccountHeaderFilter.java
+++ b/extensions/zuul/src/main/java/com/stormpath/zuul/account/ForwardedAccountHeaderFilter.java
@@ -37,7 +37,7 @@
* request, the header is not set at all.
*
* Header Name
- * If an account string is available, this filter sets the {@code X-Forwarded-Account} header with the account
+ *
If an account string is available, this filter sets the {@code X-Forwarded-User} header with the account
* string value. The header name may be changed if desired by setting the superclass
* {@link #setHeaderName(String) headerName} property. If no account/string is available, no header will be set.
*
@@ -46,7 +46,7 @@
*/
public class ForwardedAccountHeaderFilter extends AppliedRequestHeaderFilter {
- public static final String DEFAULT_HEADER_NAME = "X-Forwarded-Account";
+ public static final String DEFAULT_HEADER_NAME = "X-Forwarded-User";
private AccountResolver accountResolver;
@@ -60,7 +60,7 @@ public class ForwardedAccountHeaderFilter extends AppliedRequestHeaderFilter {
*
*
* {@code headerName}
- * {@code X-Forwarded-Account}
+ * {@code X-Forwarded-User}
*
*
* {@link #setAccountResolver(AccountResolver) accountResolver}
diff --git a/extensions/zuul/src/test/groovy/com/stormpath/zuul/account/ForwardedAccountHeaderFilterTest.groovy b/extensions/zuul/src/test/groovy/com/stormpath/zuul/account/ForwardedAccountHeaderFilterTest.groovy
index c22dae42a1..1eb451efa5 100644
--- a/extensions/zuul/src/test/groovy/com/stormpath/zuul/account/ForwardedAccountHeaderFilterTest.groovy
+++ b/extensions/zuul/src/test/groovy/com/stormpath/zuul/account/ForwardedAccountHeaderFilterTest.groovy
@@ -37,7 +37,7 @@ class ForwardedAccountHeaderFilterTest {
@Test
void testDefaults() {
def filter = new ForwardedAccountHeaderFilter()
- assertEquals filter.getHeaderName(), 'X-Forwarded-Account'
+ assertEquals filter.getHeaderName(), 'X-Forwarded-User'
assertTrue filter.accountResolver instanceof DefaultAccountResolver
assertTrue filter.getValueResolver() instanceof AccountStringResolver
}