|
| 1 | += OAuth 2.0 Changes |
| 2 | + |
| 3 | +== Validate `typ` Header with `JwtTypeValidator` |
| 4 | + |
| 5 | +`NimbusJwtDecoder` in Spring Security 7 will move `typ` header validation to `JwtTypeValidator` intsead of relying on Nimbus. |
| 6 | +This brings it in line with `NimbusJwtDecoder` validating claims instead of relying on Nimbus to validate them. |
| 7 | + |
| 8 | +If you are changing Nimbus's default type validation in a `jwtProcessorCustomizer` method, then you should move that to `JwtTypeValidator` or an implementation of `OAuth2TokenValidator` of your own. |
| 9 | + |
| 10 | +To check if you are prepared for this change, add the default `JwtTypeValidator` to your list of validators, as this will be included by default in 7: |
| 11 | + |
| 12 | +[tabs] |
| 13 | +====== |
| 14 | +Java:: |
| 15 | ++ |
| 16 | +[source,java,role="primary"] |
| 17 | +---- |
| 18 | +@Bean |
| 19 | +JwtDecoder jwtDecoder() { |
| 20 | + NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location) |
| 21 | + .validateTypes(false) <1> |
| 22 | + // ... your remaining configuration |
| 23 | + .build(); |
| 24 | + jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators( |
| 25 | + new JwtIssuerValidator(location), JwtTypeValidator.jwt())); <2> |
| 26 | + return jwtDecoder; |
| 27 | +} |
| 28 | +---- |
| 29 | +
|
| 30 | +Kotlin:: |
| 31 | ++ |
| 32 | +[source,kotlin,role="secondary"] |
| 33 | +---- |
| 34 | +@Bean |
| 35 | +fun jwtDecoder(): JwtDecoder { |
| 36 | + val jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location) |
| 37 | + .validateTypes(false) <1> |
| 38 | + // ... your remaining configuration |
| 39 | + .build() |
| 40 | + jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators( |
| 41 | + JwtIssuerValidator(location), JwtTypeValidator.jwt())) <2> |
| 42 | + return jwtDecoder |
| 43 | +} |
| 44 | +---- |
| 45 | +====== |
| 46 | +<1> - Switch off Nimbus verifying the `typ` (this will be off by default in 7) |
| 47 | +<2> - Add the default `typ` validator (this will be included by default in 7) |
| 48 | + |
| 49 | +Note the default value verifies that the `typ` value either be `JWT` or not present, which is the same as the Nimbus default. |
| 50 | +It is also aligned with https://datatracker.ietf.org/doc/html/rfc7515#section-4.1.9[RFC 7515] which states that `typ` is optional. |
| 51 | + |
| 52 | + |
| 53 | +=== I'm Using A `DefaultJOSEObjectTypeVerifier` |
| 54 | + |
| 55 | +If you have something like the following in your configuration: |
| 56 | + |
| 57 | +[tabs] |
| 58 | +====== |
| 59 | +Java:: |
| 60 | ++ |
| 61 | +[source,java,role="primary"] |
| 62 | +---- |
| 63 | +@Bean |
| 64 | +JwtDecoder jwtDecoder() { |
| 65 | + NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location) |
| 66 | + .jwtProcessorCustomizer((c) -> c |
| 67 | + .setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier<>("JOSE")) |
| 68 | + ) |
| 69 | + .build(); |
| 70 | + return jwtDecoder; |
| 71 | +} |
| 72 | +---- |
| 73 | +
|
| 74 | +Kotlin:: |
| 75 | ++ |
| 76 | +[source,kotlin,role="secondary"] |
| 77 | +---- |
| 78 | +@Bean |
| 79 | +fun jwtDecoder(): JwtDecoder { |
| 80 | + val jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location) |
| 81 | + .jwtProcessorCustomizer { |
| 82 | + it.setJWSTypeVerifier(DefaultJOSEObjectTypeVerifier("JOSE")) |
| 83 | + } |
| 84 | + .build() |
| 85 | + return jwtDecoder |
| 86 | +} |
| 87 | +---- |
| 88 | +====== |
| 89 | + |
| 90 | +Then change this to: |
| 91 | + |
| 92 | +[tabs] |
| 93 | +====== |
| 94 | +Java:: |
| 95 | ++ |
| 96 | +[source,java,role="primary"] |
| 97 | +---- |
| 98 | +@Bean |
| 99 | +JwtDecoder jwtDecoder() { |
| 100 | + NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location) |
| 101 | + .validateTypes(false) |
| 102 | + .build(); |
| 103 | + jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators( |
| 104 | + new JwtIssuerValidator(location), new JwtTypeValidator("JOSE"))); |
| 105 | + return jwtDecoder; |
| 106 | +} |
| 107 | +---- |
| 108 | +
|
| 109 | +Kotlin:: |
| 110 | ++ |
| 111 | +[source,kotlin,role="secondary"] |
| 112 | +---- |
| 113 | +@Bean |
| 114 | +fun jwtDecoder(): JwtDecoder { |
| 115 | + val jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location) |
| 116 | + .validateTypes(false) |
| 117 | + .build() |
| 118 | + jwtDecoder.setJwtValidator(JwtValidators.createDefaultWithValidators( |
| 119 | + JwtIssuerValidator(location), JwtTypeValidator("JOSE"))) |
| 120 | + return jwtDecoder |
| 121 | +} |
| 122 | +---- |
| 123 | +====== |
| 124 | + |
| 125 | +To indicate that the `typ` header is optional, use `#setAllowEmpty(true)` (this is the equivalent of including `null` in the list of allowed types in `DefaultJOSEObjectTypeVerifier`). |
| 126 | + |
| 127 | +=== I want to opt-out |
| 128 | + |
| 129 | +If you want to keep doing things the way that you are, then the steps are similar, just in reverse: |
| 130 | + |
| 131 | +[tabs] |
| 132 | +====== |
| 133 | +Java:: |
| 134 | ++ |
| 135 | +[source,java,role="primary"] |
| 136 | +---- |
| 137 | +@Bean |
| 138 | +JwtDecoder jwtDecoder() { |
| 139 | + NimbusJwtDecoder jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location) |
| 140 | + .validateTypes(true) <1> |
| 141 | + .jwtProcessorCustomizer((c) -> c |
| 142 | + .setJWSTypeVerifier(new DefaultJOSEObjectTypeVerifier<>("JOSE")) |
| 143 | + ) |
| 144 | + .build(); |
| 145 | + jwtDecoder.setJwtValidator(new DelegatingOAuth2TokenValidator<>( |
| 146 | + new JwtTimestampValidator(), new JwtIssuerValidator(location))); <2> |
| 147 | + return jwtDecoder; |
| 148 | +} |
| 149 | +---- |
| 150 | +
|
| 151 | +Kotlin:: |
| 152 | ++ |
| 153 | +[source,kotlin,role="secondary"] |
| 154 | +---- |
| 155 | +@Bean |
| 156 | +fun jwtDecoder(): JwtDecoder { |
| 157 | + val jwtDecoder = NimbusJwtDecoder.withIssuerLocation(location) |
| 158 | + .validateTypes(true) <1> |
| 159 | + .jwtProcessorCustomizer { |
| 160 | + it.setJWSTypeVerifier(DefaultJOSEObjectTypeVerifier("JOSE")) |
| 161 | + } |
| 162 | + .build() |
| 163 | + jwtDecoder.setJwtValidator(DelegatingOAuth2TokenValidator( |
| 164 | + JwtTimestampValidator(), JwtIssuerValidator(location))) <2> |
| 165 | + return jwtDecoder |
| 166 | +} |
| 167 | +---- |
| 168 | +====== |
| 169 | +<1> - leave Nimbus type verification on |
| 170 | +<2> - specify the list of validators you need, excluding `JwtTypeValidator` |
| 171 | + |
| 172 | +For additional guidance, please see the xref:servlet/oauth2/resource-server/jwt.adoc#oauth2resourceserver-jwt-validation[JwtDecoder Validators] section in the reference. |
0 commit comments