oauth2.adoc 5.0 KB

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