├── .gitignore
├── CHANGELOG.md
├── LICENSE
├── NOTICES
├── pom.xml
├── readme.md
└── src
├── main
└── java
│ └── io
│ └── curity
│ └── oauth
│ ├── AbstractJwtValidator.java
│ ├── AuthenticatedUser.java
│ ├── AuthenticatedUserRequestWrapper.java
│ ├── DefaultHttpClientProvider.java
│ ├── DefaultIntrospectClient.java
│ ├── DefaultWebKeysClient.java
│ ├── EdDSAPublicKeyCreator.java
│ ├── Expirable.java
│ ├── ExpirationBasedCache.java
│ ├── FilterHelper.java
│ ├── HttpClientProvider.java
│ ├── IntrospectionClient.java
│ ├── JsonData.java
│ ├── JsonUtils.java
│ ├── JsonWebKey.java
│ ├── JsonWebKeyNotFoundException.java
│ ├── JsonWebKeyType.java
│ ├── JwkManager.java
│ ├── JwksResponse.java
│ ├── JwtValidator.java
│ ├── JwtValidatorWithCert.java
│ ├── JwtValidatorWithJwk.java
│ ├── OAuthFilter.java
│ ├── OAuthIntrospectResponse.java
│ ├── OAuthJwtFilter.java
│ ├── OAuthOpaqueFilter.java
│ ├── OpaqueTokenValidator.java
│ ├── RsaPublicKeyCreator.java
│ ├── TimeBasedCache.java
│ ├── TokenValidationException.java
│ ├── TokenValidator.java
│ ├── ValidationExceptions.java
│ └── WebKeysClient.java
└── test
├── java
└── io
│ └── curity
│ └── oauth
│ ├── AuthenticatedUserRequestWrapperTest.java
│ ├── HttpClientProviderTest.java
│ ├── JwtTokenIssuer.java
│ ├── JwtWithCertTest.java
│ ├── JwtWithJwksTest.java
│ └── TimeBasedCacheTest.java
└── resources
├── META-INF
└── services
│ └── io.curity.oauth.HttpClientProvider
├── Se.Curity.Test.p12
└── log4j2-test.xml
/.gitignore:
--------------------------------------------------------------------------------
1 | target
2 | lib
3 | *.sublime-workspace
4 |
5 | # Logs and databases #
6 | ######################
7 | *.log
8 | *.sqlite
9 |
10 | # Compiled files #
11 | ######################
12 | *.pyc
13 | *.class
14 |
15 | # OS generated files #
16 | ######################
17 | .DS_Store
18 | .DS_Store?
19 | ._*
20 | .Spotlight-V100
21 | .Trashes
22 | ehthumbs.db
23 | Thumbs.db
24 |
25 | # Eclipse files #
26 | #################
27 | .classpath
28 | .project
29 | .settings
30 |
31 |
32 | # MS Word files #
33 | ~$*.doc*
34 | ~WRL*.tmp
35 |
36 | # Ignore build dir and dist package
37 | .idea
38 | *.iml
39 | =======
40 | .idea
41 | *.iml
42 |
43 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | ## OAuth Filter for Java Changelog
2 |
3 | ## Unreleased
4 |
5 | ## 3.0.0 (2021-05-27)
6 |
7 | Features:
8 | - Changed groupID to `io.curity` and package name to `io.curity.oauth`.
9 | - Updated dependencies.
10 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Apache License
2 | Version 2.0, January 2004
3 | http://www.apache.org/licenses/
4 |
5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
6 |
7 | 1. Definitions.
8 |
9 | "License" shall mean the terms and conditions for use, reproduction,
10 | and distribution as defined by Sections 1 through 9 of this document.
11 |
12 | "Licensor" shall mean the copyright owner or entity authorized by
13 | the copyright owner that is granting the License.
14 |
15 | "Legal Entity" shall mean the union of the acting entity and all
16 | other entities that control, are controlled by, or are under common
17 | control with that entity. For the purposes of this definition,
18 | "control" means (i) the power, direct or indirect, to cause the
19 | direction or management of such entity, whether by contract or
20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
21 | outstanding shares, or (iii) beneficial ownership of such entity.
22 |
23 | "You" (or "Your") shall mean an individual or Legal Entity
24 | exercising permissions granted by this License.
25 |
26 | "Source" form shall mean the preferred form for making modifications,
27 | including but not limited to software source code, documentation
28 | source, and configuration files.
29 |
30 | "Object" form shall mean any form resulting from mechanical
31 | transformation or translation of a Source form, including but
32 | not limited to compiled object code, generated documentation,
33 | and conversions to other media types.
34 |
35 | "Work" shall mean the work of authorship, whether in Source or
36 | Object form, made available under the License, as indicated by a
37 | copyright notice that is included in or attached to the work
38 | (an example is provided in the Appendix below).
39 |
40 | "Derivative Works" shall mean any work, whether in Source or Object
41 | form, that is based on (or derived from) the Work and for which the
42 | editorial revisions, annotations, elaborations, or other modifications
43 | represent, as a whole, an original work of authorship. For the purposes
44 | of this License, Derivative Works shall not include works that remain
45 | separable from, or merely link (or bind by name) to the interfaces of,
46 | the Work and Derivative Works thereof.
47 |
48 | "Contribution" shall mean any work of authorship, including
49 | the original version of the Work and any modifications or additions
50 | to that Work or Derivative Works thereof, that is intentionally
51 | submitted to Licensor for inclusion in the Work by the copyright owner
52 | or by an individual or Legal Entity authorized to submit on behalf of
53 | the copyright owner. For the purposes of this definition, "submitted"
54 | means any form of electronic, verbal, or written communication sent
55 | to the Licensor or its representatives, including but not limited to
56 | communication on electronic mailing lists, source code control systems,
57 | and issue tracking systems that are managed by, or on behalf of, the
58 | Licensor for the purpose of discussing and improving the Work, but
59 | excluding communication that is conspicuously marked or otherwise
60 | designated in writing by the copyright owner as "Not a Contribution."
61 |
62 | "Contributor" shall mean Licensor and any individual or Legal Entity
63 | on behalf of whom a Contribution has been received by Licensor and
64 | subsequently incorporated within the Work.
65 |
66 | 2. Grant of Copyright License. Subject to the terms and conditions of
67 | this License, each Contributor hereby grants to You a perpetual,
68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
69 | copyright license to reproduce, prepare Derivative Works of,
70 | publicly display, publicly perform, sublicense, and distribute the
71 | Work and such Derivative Works in Source or Object form.
72 |
73 | 3. Grant of Patent License. Subject to the terms and conditions of
74 | this License, each Contributor hereby grants to You a perpetual,
75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
76 | (except as stated in this section) patent license to make, have made,
77 | use, offer to sell, sell, import, and otherwise transfer the Work,
78 | where such license applies only to those patent claims licensable
79 | by such Contributor that are necessarily infringed by their
80 | Contribution(s) alone or by combination of their Contribution(s)
81 | with the Work to which such Contribution(s) was submitted. If You
82 | institute patent litigation against any entity (including a
83 | cross-claim or counterclaim in a lawsuit) alleging that the Work
84 | or a Contribution incorporated within the Work constitutes direct
85 | or contributory patent infringement, then any patent licenses
86 | granted to You under this License for that Work shall terminate
87 | as of the date such litigation is filed.
88 |
89 | 4. Redistribution. You may reproduce and distribute copies of the
90 | Work or Derivative Works thereof in any medium, with or without
91 | modifications, and in Source or Object form, provided that You
92 | meet the following conditions:
93 |
94 | (a) You must give any other recipients of the Work or
95 | Derivative Works a copy of this License; and
96 |
97 | (b) You must cause any modified files to carry prominent notices
98 | stating that You changed the files; and
99 |
100 | (c) You must retain, in the Source form of any Derivative Works
101 | that You distribute, all copyright, patent, trademark, and
102 | attribution notices from the Source form of the Work,
103 | excluding those notices that do not pertain to any part of
104 | the Derivative Works; and
105 |
106 | (d) If the Work includes a "NOTICE" text file as part of its
107 | distribution, then any Derivative Works that You distribute must
108 | include a readable copy of the attribution notices contained
109 | within such NOTICE file, excluding those notices that do not
110 | pertain to any part of the Derivative Works, in at least one
111 | of the following places: within a NOTICE text file distributed
112 | as part of the Derivative Works; within the Source form or
113 | documentation, if provided along with the Derivative Works; or,
114 | within a display generated by the Derivative Works, if and
115 | wherever such third-party notices normally appear. The contents
116 | of the NOTICE file are for informational purposes only and
117 | do not modify the License. You may add Your own attribution
118 | notices within Derivative Works that You distribute, alongside
119 | or as an addendum to the NOTICE text from the Work, provided
120 | that such additional attribution notices cannot be construed
121 | as modifying the License.
122 |
123 | You may add Your own copyright statement to Your modifications and
124 | may provide additional or different license terms and conditions
125 | for use, reproduction, or distribution of Your modifications, or
126 | for any such Derivative Works as a whole, provided Your use,
127 | reproduction, and distribution of the Work otherwise complies with
128 | the conditions stated in this License.
129 |
130 | 5. Submission of Contributions. Unless You explicitly state otherwise,
131 | any Contribution intentionally submitted for inclusion in the Work
132 | by You to the Licensor shall be under the terms and conditions of
133 | this License, without any additional terms or conditions.
134 | Notwithstanding the above, nothing herein shall supersede or modify
135 | the terms of any separate license agreement you may have executed
136 | with Licensor regarding such Contributions.
137 |
138 | 6. Trademarks. This License does not grant permission to use the trade
139 | names, trademarks, service marks, or product names of the Licensor,
140 | except as required for reasonable and customary use in describing the
141 | origin of the Work and reproducing the content of the NOTICE file.
142 |
143 | 7. Disclaimer of Warranty. Unless required by applicable law or
144 | agreed to in writing, Licensor provides the Work (and each
145 | Contributor provides its Contributions) on an "AS IS" BASIS,
146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
147 | implied, including, without limitation, any warranties or conditions
148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
149 | PARTICULAR PURPOSE. You are solely responsible for determining the
150 | appropriateness of using or redistributing the Work and assume any
151 | risks associated with Your exercise of permissions under this License.
152 |
153 | 8. Limitation of Liability. In no event and under no legal theory,
154 | whether in tort (including negligence), contract, or otherwise,
155 | unless required by applicable law (such as deliberate and grossly
156 | negligent acts) or agreed to in writing, shall any Contributor be
157 | liable to You for damages, including any direct, indirect, special,
158 | incidental, or consequential damages of any character arising as a
159 | result of this License or out of the use or inability to use the
160 | Work (including but not limited to damages for loss of goodwill,
161 | work stoppage, computer failure or malfunction, or any and all
162 | other commercial damages or losses), even if such Contributor
163 | has been advised of the possibility of such damages.
164 |
165 | 9. Accepting Warranty or Additional Liability. While redistributing
166 | the Work or Derivative Works thereof, You may choose to offer,
167 | and charge a fee for, acceptance of support, warranty, indemnity,
168 | or other liability obligations and/or rights consistent with this
169 | License. However, in accepting such obligations, You may act only
170 | on Your own behalf and on Your sole responsibility, not on behalf
171 | of any other Contributor, and only if You agree to indemnify,
172 | defend, and hold each Contributor harmless for any liability
173 | incurred by, or claims asserted against, such Contributor by reason
174 | of your accepting any such warranty or additional liability.
175 |
176 | END OF TERMS AND CONDITIONS
177 |
178 | APPENDIX: How to apply the Apache License to your work.
179 |
180 | To apply the Apache License to your work, attach the following
181 | boilerplate notice, with the fields enclosed by brackets "[]"
182 | replaced with your own identifying information. (Don't include
183 | the brackets!) The text should be enclosed in the appropriate
184 | comment syntax for the file format. We also recommend that a
185 | file or class name and description of purpose be included on the
186 | same "printed page" as the copyright notice for easier
187 | identification within third-party archives.
188 |
189 | Copyright [yyyy] [name of copyright owner]
190 |
191 | Licensed under the Apache License, Version 2.0 (the "License");
192 | you may not use this file except in compliance with the License.
193 | You may obtain a copy of the License at
194 |
195 | http://www.apache.org/licenses/LICENSE-2.0
196 |
197 | Unless required by applicable law or agreed to in writing, software
198 | distributed under the License is distributed on an "AS IS" BASIS,
199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
200 | See the License for the specific language governing permissions and
201 | limitations under the License.
202 |
--------------------------------------------------------------------------------
/NOTICES:
--------------------------------------------------------------------------------
1 | # Attributions, Copyrights, and Licenses
2 |
3 |
4 |
5 | ## Definitions
6 |
7 | * "**Curity**" means Curity AB, a Swedish corporation
8 | * "**Software**" means the software program that this document is included with as provided by Curity
9 |
10 | ## Content
11 |
12 | This document contains attributions, copyright notices, and links to the software licenses of third-party open source software libraries used by the Software.
13 |
14 | ## License
15 |
16 | The Software is licensed under the terms of the Apache v. 2 license agreement.
17 |
18 | ## Included Components
19 |
20 | The Software makes use of the following third-party open source software libraries.
21 |
22 | ### Apache HttpComponents
23 |
24 | * License: Apache v. 2
25 | * Modifications: No
26 | * Link: https://hc.apache.org/
27 | * Notices:
28 |
29 |
30 | Apache HttpComponents Client
31 | Copyright 1999-2015 The Apache Software Foundation
32 |
33 | This product includes software developed at
34 | The Apache Software Foundation (http://www.apache.org/).
35 |
Copyright (c) 2011-2013 Oracle and/or its affiliates. All rights reserved.
54 |
55 | ## Optionally Included Components
56 |
57 | If the Software is obtained in source code form, the following third-party open source software libraries are also used by the Software.
58 |
59 | ### Apache Log4j 2
60 |
61 | * License: Apache v. 2
62 | * Modifications: No
63 | * Link: http://logging.apache.org/log4j/
64 | * Notices:
65 |
66 |
67 | Apache Log4j
68 | Copyright 1999-2017 Apache Software Foundation
69 |
70 | This product includes software developed at
71 | The Apache Software Foundation (http://www.apache.org/).
72 |
73 | ResolverUtil.java
74 | Copyright 2005-2006 Tim Fennell
75 |
76 | Dumbster SMTP test server
77 | Copyright 2004 Jason Paul Kitchen
78 |
79 | TypeUtil.java
80 | Copyright 2002-2012 Ramnivas Laddad, Juergen Hoeller, Chris Beams
81 |
100 | jose4j
101 | Copyright 2012-2015 Brian Campbell
102 |
103 | EcdsaUsingShaAlgorithm contains code for converting the concatenated
104 | R & S values of the signature to and from DER, which was originally
105 | derived from the Apache Santuario XML Security library's SignatureECDSA
106 | implementation. http://santuario.apache.org/
107 |
108 | The Base64 implementation in this software was derived from the
109 | Apache Commons Codec project. http://commons.apache.org/proper/commons-codec/
110 |
111 | JSON processing in this software was derived from the JSON.simple toolkit.
112 | https://code.google.com/p/json-simple/
113 |
134 | Apache License
135 | Version 2.0, January 2004
136 | http://www.apache.org/licenses/
137 |
138 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
139 |
140 | 1. Definitions.
141 |
142 | "License" shall mean the terms and conditions for use, reproduction,
143 | and distribution as defined by Sections 1 through 9 of this document.
144 |
145 | "Licensor" shall mean the copyright owner or entity authorized by
146 | the copyright owner that is granting the License.
147 |
148 | "Legal Entity" shall mean the union of the acting entity and all
149 | other entities that control, are controlled by, or are under common
150 | control with that entity. For the purposes of this definition,
151 | "control" means (i) the power, direct or indirect, to cause the
152 | direction or management of such entity, whether by contract or
153 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
154 | outstanding shares, or (iii) beneficial ownership of such entity.
155 |
156 | "You" (or "Your") shall mean an individual or Legal Entity
157 | exercising permissions granted by this License.
158 |
159 | "Source" form shall mean the preferred form for making modifications,
160 | including but not limited to software source code, documentation
161 | source, and configuration files.
162 |
163 | "Object" form shall mean any form resulting from mechanical
164 | transformation or translation of a Source form, including but
165 | not limited to compiled object code, generated documentation,
166 | and conversions to other media types.
167 |
168 | "Work" shall mean the work of authorship, whether in Source or
169 | Object form, made available under the License, as indicated by a
170 | copyright notice that is included in or attached to the work
171 | (an example is provided in the Appendix below).
172 |
173 | "Derivative Works" shall mean any work, whether in Source or Object
174 | form, that is based on (or derived from) the Work and for which the
175 | editorial revisions, annotations, elaborations, or other modifications
176 | represent, as a whole, an original work of authorship. For the purposes
177 | of this License, Derivative Works shall not include works that remain
178 | separable from, or merely link (or bind by name) to the interfaces of,
179 | the Work and Derivative Works thereof.
180 |
181 | "Contribution" shall mean any work of authorship, including
182 | the original version of the Work and any modifications or additions
183 | to that Work or Derivative Works thereof, that is intentionally
184 | submitted to Licensor for inclusion in the Work by the copyright owner
185 | or by an individual or Legal Entity authorized to submit on behalf of
186 | the copyright owner. For the purposes of this definition, "submitted"
187 | means any form of electronic, verbal, or written communication sent
188 | to the Licensor or its representatives, including but not limited to
189 | communication on electronic mailing lists, source code control systems,
190 | and issue tracking systems that are managed by, or on behalf of, the
191 | Licensor for the purpose of discussing and improving the Work, but
192 | excluding communication that is conspicuously marked or otherwise
193 | designated in writing by the copyright owner as "Not a Contribution."
194 |
195 | "Contributor" shall mean Licensor and any individual or Legal Entity
196 | on behalf of whom a Contribution has been received by Licensor and
197 | subsequently incorporated within the Work.
198 |
199 | 2. Grant of Copyright License. Subject to the terms and conditions of
200 | this License, each Contributor hereby grants to You a perpetual,
201 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
202 | copyright license to reproduce, prepare Derivative Works of,
203 | publicly display, publicly perform, sublicense, and distribute the
204 | Work and such Derivative Works in Source or Object form.
205 |
206 | 3. Grant of Patent License. Subject to the terms and conditions of
207 | this License, each Contributor hereby grants to You a perpetual,
208 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
209 | (except as stated in this section) patent license to make, have made,
210 | use, offer to sell, sell, import, and otherwise transfer the Work,
211 | where such license applies only to those patent claims licensable
212 | by such Contributor that are necessarily infringed by their
213 | Contribution(s) alone or by combination of their Contribution(s)
214 | with the Work to which such Contribution(s) was submitted. If You
215 | institute patent litigation against any entity (including a
216 | cross-claim or counterclaim in a lawsuit) alleging that the Work
217 | or a Contribution incorporated within the Work constitutes direct
218 | or contributory patent infringement, then any patent licenses
219 | granted to You under this License for that Work shall terminate
220 | as of the date such litigation is filed.
221 |
222 | 4. Redistribution. You may reproduce and distribute copies of the
223 | Work or Derivative Works thereof in any medium, with or without
224 | modifications, and in Source or Object form, provided that You
225 | meet the following conditions:
226 |
227 | (a) You must give any other recipients of the Work or
228 | Derivative Works a copy of this License; and
229 |
230 | (b) You must cause any modified files to carry prominent notices
231 | stating that You changed the files; and
232 |
233 | (c) You must retain, in the Source form of any Derivative Works
234 | that You distribute, all copyright, patent, trademark, and
235 | attribution notices from the Source form of the Work,
236 | excluding those notices that do not pertain to any part of
237 | the Derivative Works; and
238 |
239 | (d) If the Work includes a "NOTICE" text file as part of its
240 | distribution, then any Derivative Works that You distribute must
241 | include a readable copy of the attribution notices contained
242 | within such NOTICE file, excluding those notices that do not
243 | pertain to any part of the Derivative Works, in at least one
244 | of the following places: within a NOTICE text file distributed
245 | as part of the Derivative Works; within the Source form or
246 | documentation, if provided along with the Derivative Works; or,
247 | within a display generated by the Derivative Works, if and
248 | wherever such third-party notices normally appear. The contents
249 | of the NOTICE file are for informational purposes only and
250 | do not modify the License. You may add Your own attribution
251 | notices within Derivative Works that You distribute, alongside
252 | or as an addendum to the NOTICE text from the Work, provided
253 | that such additional attribution notices cannot be construed
254 | as modifying the License.
255 |
256 | You may add Your own copyright statement to Your modifications and
257 | may provide additional or different license terms and conditions
258 | for use, reproduction, or distribution of Your modifications, or
259 | for any such Derivative Works as a whole, provided Your use,
260 | reproduction, and distribution of the Work otherwise complies with
261 | the conditions stated in this License.
262 |
263 | 5. Submission of Contributions. Unless You explicitly state otherwise,
264 | any Contribution intentionally submitted for inclusion in the Work
265 | by You to the Licensor shall be under the terms and conditions of
266 | this License, without any additional terms or conditions.
267 | Notwithstanding the above, nothing herein shall supersede or modify
268 | the terms of any separate license agreement you may have executed
269 | with Licensor regarding such Contributions.
270 |
271 | 6. Trademarks. This License does not grant permission to use the trade
272 | names, trademarks, service marks, or product names of the Licensor,
273 | except as required for reasonable and customary use in describing the
274 | origin of the Work and reproducing the content of the NOTICE file.
275 |
276 | 7. Disclaimer of Warranty. Unless required by applicable law or
277 | agreed to in writing, Licensor provides the Work (and each
278 | Contributor provides its Contributions) on an "AS IS" BASIS,
279 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
280 | implied, including, without limitation, any warranties or conditions
281 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
282 | PARTICULAR PURPOSE. You are solely responsible for determining the
283 | appropriateness of using or redistributing the Work and assume any
284 | risks associated with Your exercise of permissions under this License.
285 |
286 | 8. Limitation of Liability. In no event and under no legal theory,
287 | whether in tort (including negligence), contract, or otherwise,
288 | unless required by applicable law (such as deliberate and grossly
289 | negligent acts) or agreed to in writing, shall any Contributor be
290 | liable to You for damages, including any direct, indirect, special,
291 | incidental, or consequential damages of any character arising as a
292 | result of this License or out of the use or inability to use the
293 | Work (including but not limited to damages for loss of goodwill,
294 | work stoppage, computer failure or malfunction, or any and all
295 | other commercial damages or losses), even if such Contributor
296 | has been advised of the possibility of such damages.
297 |
298 | 9. Accepting Warranty or Additional Liability. While redistributing
299 | the Work or Derivative Works thereof, You may choose to offer,
300 | and charge a fee for, acceptance of support, warranty, indemnity,
301 | or other liability obligations and/or rights consistent with this
302 | License. However, in accepting such obligations, You may act only
303 | on Your own behalf and on Your sole responsibility, not on behalf
304 | of any other Contributor, and only if You agree to indemnify,
305 | defend, and hold each Contributor harmless for any liability
306 | incurred by, or claims asserted against, such Contributor by reason
307 | of your accepting any such warranty or additional liability.
308 |
309 | END OF TERMS AND CONDITIONS
310 |
311 | APPENDIX: How to apply the Apache License to your work.
312 |
313 | To apply the Apache License to your work, attach the following
314 | boilerplate notice, with the fields enclosed by brackets "[]"
315 | replaced with your own identifying information. (Don't include
316 | the brackets!) The text should be enclosed in the appropriate
317 | comment syntax for the file format. We also recommend that a
318 | file or class name and description of purpose be included on the
319 | same "printed page" as the copyright notice for easier
320 | identification within third-party archives.
321 |
322 | Copyright [yyyy] [name of copyright owner]
323 |
324 | Licensed under the Apache License, Version 2.0 (the "License");
325 | you may not use this file except in compliance with the License.
326 | You may obtain a copy of the License at
327 |
328 | http://www.apache.org/licenses/LICENSE-2.0
329 |
330 | Unless required by applicable law or agreed to in writing, software
331 | distributed under the License is distributed on an "AS IS" BASIS,
332 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
333 | See the License for the specific language governing permissions and
334 | limitations under the License.
335 |
336 |
337 | ### CDDL v. 1
338 |
339 |
340 | COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL) Version 1.0
341 |
342 | 1. Definitions.
343 |
344 | 1.1. Contributor. means each individual or entity that creates or contributes to the creation of Modifications.
345 |
346 | 1.2. Contributor Version. means the combination of the Original Software, prior Modifications used by a Contributor (if any), and the Modifications made by that particular Contributor.
347 |
348 | 1.3. Covered Software. means (a) the Original Software, or (b) Modifications, or (c) the combination of files containing Original Software with files containing Modifications, in each case including portions thereof.
349 |
350 | 1.4. Executable. means the Covered Software in any form other than Source Code.
351 |
352 | 1.5. Initial Developer. means the individual or entity that first makes Original Software available under this License.
353 |
354 | 1.6. Larger Work. means a work which combines Covered Software or portions thereof with code not governed by the terms of this License.
355 |
356 | 1.7. License. means this document.
357 |
358 | 1.8. Licensable. means having the right to grant, to the maximum extent possible, whether at the time of the initial grant or subsequently acquired, any and all of the rights conveyed herein.
359 |
360 | 1.9. Modifications. means the Source Code and Executable form of any of the following:
361 |
362 | A. Any file that results from an addition to, deletion from or modification of the contents of a file containing Original Software or previous Modifications;
363 |
364 | B. Any new file that contains any part of the Original Software or previous Modification; or
365 |
366 | C. Any new file that is contributed or otherwise made available under the terms of this License.
367 |
368 | 1.10. Original Software. means the Source Code and Executable form of computer software code that is originally released under this License.
369 |
370 | 1.11. Patent Claims. means any patent claim(s), now owned or hereafter acquired, including without limitation, method, process, and apparatus claims, in any patent Licensable by grantor.
371 |
372 | 1.12. Source Code. means (a) the common form of computer software code in which modifications are made and (b) associated documentation included in or with such code.
373 |
374 | 1.13. You. (or .Your.) means an individual or a legal entity exercising rights under, and complying with all of the terms of, this License. For legal entities, .You. includes any entity which controls, is controlled by, or is under common control with You. For purposes of this definition, .control. means (a) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (b) ownership of more than fifty percent (50%) of the outstanding shares or beneficial ownership of such entity.
375 |
376 | 2. License Grants.
377 |
378 | 2.1. The Initial Developer Grant.
379 |
380 | Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, the Initial Developer hereby grants You a world-wide, royalty-free, non-exclusive license:
381 |
382 | (a) under intellectual property rights (other than patent or trademark) Licensable by Initial Developer, to use, reproduce, modify, display, perform, sublicense and distribute the Original Software (or portions thereof), with or without Modifications, and/or as part of a Larger Work; and
383 |
384 | (b) under Patent Claims infringed by the making, using or selling of Original Software, to make, have made, use, practice, sell, and offer for sale, and/or otherwise dispose of the Original Software (or portions thereof).
385 |
386 | (c) The licenses granted in Sections 2.1(a) and (b) are effective on the date Initial Developer first distributes or otherwise makes the Original Software available to a third party under the terms of this License.
387 |
388 | (d) Notwithstanding Section 2.1(b) above, no patent license is granted: (1) for code that You delete from the Original Software, or (2) for infringements caused by: (i) the modification of the Original Software, or (ii) the combination of the Original Software with other software or devices.
389 |
390 | 2.2. Contributor Grant.
391 |
392 | Conditioned upon Your compliance with Section 3.1 below and subject to third party intellectual property claims, each Contributor hereby grants You a world-wide, royalty-free, non-exclusive license:
393 |
394 | (a) under intellectual property rights (other than patent or trademark) Licensable by Contributor to use, reproduce, modify, display, perform, sublicense and distribute the Modifications created by such Contributor (or portions thereof), either on an unmodified basis, with other Modifications, as Covered Software and/or as part of a Larger Work; and
395 |
396 | (b) under Patent Claims infringed by the making, using, or selling of Modifications made by that Contributor either alone and/or in combination with its Contributor Version (or portions of such combination), to make, use, sell, offer for sale, have made, and/or otherwise dispose of: (1) Modifications made by that Contributor (or portions thereof); and (2) the combination of Modifications made by that Contributor with its Contributor Version (or portions of such combination).
397 |
398 | (c) The licenses granted in Sections 2.2(a) and 2.2(b) are effective on the date Contributor first distributes or otherwise makes the Modifications available to a third party.
399 |
400 | (d) Notwithstanding Section 2.2(b) above, no patent license is granted: (1) for any code that Contributor has deleted from the Contributor Version; (2) for infringements caused by: (i) third party modifications of Contributor Version, or (ii) the combination of Modifications made by that Contributor with other software (except as part of the Contributor Version) or other devices; or (3) under Patent Claims infringed by Covered Software in the absence of Modifications made by that Contributor.
401 |
402 | 3. Distribution Obligations.
403 |
404 | 3.1. Availability of Source Code.
405 | Any Covered Software that You distribute or otherwise make available in Executable form must also be made available in Source Code form and that Source Code form must be distributed only under the terms of this License. You must include a copy of this License with every copy of the Source Code form of the Covered Software You distribute or otherwise make available. You must inform recipients of any such Covered Software in Executable form as to how they can obtain such Covered Software in Source Code form in a reasonable manner on or through a medium customarily used for software exchange.
406 |
407 | 3.2. Modifications.
408 | The Modifications that You create or to which You contribute are governed by the terms of this License. You represent that You believe Your Modifications are Your original creation(s) and/or You have sufficient rights to grant the rights conveyed by this License.
409 |
410 | 3.3. Required Notices.
411 | You must include a notice in each of Your Modifications that identifies You as the Contributor of the Modification. You may not remove or alter any copyright, patent or trademark notices contained within the Covered Software, or any notices of licensing or any descriptive text giving attribution to any Contributor or the Initial Developer.
412 |
413 | 3.4. Application of Additional Terms.
414 | You may not offer or impose any terms on any Covered Software in Source Code form that alters or restricts the applicable version of this License or the recipients. rights hereunder. You may choose to offer, and to charge a fee for, warranty, support, indemnity or liability obligations to one or more recipients of Covered Software. However, you may do so only on Your own behalf, and not on behalf of the Initial Developer or any Contributor. You must make it absolutely clear that any such warranty, support, indemnity or liability obligation is offered by You alone, and You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of warranty, support, indemnity or liability terms You offer.
415 |
416 | 3.5. Distribution of Executable Versions.
417 | You may distribute the Executable form of the Covered Software under the terms of this License or under the terms of a license of Your choice, which may contain terms different from this License, provided that You are in compliance with the terms of this License and that the license for the Executable form does not attempt to limit or alter the recipient.s rights in the Source Code form from the rights set forth in this License. If You distribute the Covered Software in Executable form under a different license, You must make it absolutely clear that any terms which differ from this License are offered by You alone, not by the Initial Developer or Contributor. You hereby agree to indemnify the Initial Developer and every Contributor for any liability incurred by the Initial Developer or such Contributor as a result of any such terms You offer.
418 |
419 | 3.6. Larger Works.
420 | You may create a Larger Work by combining Covered Software with other code not governed by the terms of this License and distribute the Larger Work as a single product. In such a case, You must make sure the requirements of this License are fulfilled for the Covered Software.
421 |
422 | 4. Versions of the License.
423 |
424 | 4.1. New Versions.
425 | Sun Microsystems, Inc. is the initial license steward and may publish revised and/or new versions of this License from time to time. Each version will be given a distinguishing version number. Except as provided in Section 4.3, no one other than the license steward has the right to modify this License.
426 |
427 | 4.2. Effect of New Versions.
428 | You may always continue to use, distribute or otherwise make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. If the Initial Developer includes a notice in the Original Software prohibiting it from being distributed or otherwise made available under any subsequent version of the License, You must distribute and make the Covered Software available under the terms of the version of the License under which You originally received the Covered Software. Otherwise, You may also choose to use, distribute or otherwise make the Covered Software available under the terms of any subsequent version of the License published by the license steward.
429 |
430 | 4.3. Modified Versions.
431 | When You are an Initial Developer and You want to create a new license for Your Original Software, You may create and use a modified version of this License if You: (a) rename the license and remove any references to the name of the license steward (except to note that the license differs from this License); and (b) otherwise make it clear that the license contains terms which differ from this License.
432 |
433 | 5. DISCLAIMER OF WARRANTY.
434 |
435 | COVERED SOFTWARE IS PROVIDED UNDER THIS LICENSE ON AN .AS IS. BASIS, WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES THAT THE COVERED SOFTWARE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED SOFTWARE IS WITH YOU. SHOULD ANY COVERED SOFTWARE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF ANY COVERED SOFTWARE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER.
436 |
437 | 6. TERMINATION.
438 |
439 | 6.1. This License and the rights granted hereunder will terminate automatically if You fail to comply with terms herein and fail to cure such breach within 30 days of becoming aware of the breach. Provisions which, by their nature, must remain in effect beyond the termination of this License shall survive.
440 |
441 | 6.2. If You assert a patent infringement claim (excluding declaratory judgment actions) against Initial Developer or a Contributor (the Initial Developer or Contributor against whom You assert such claim is referred to as .Participant.) alleging that the Participant Software (meaning the Contributor Version where the Participant is a Contributor or the Original Software where the Participant is the Initial Developer) directly or indirectly infringes any patent, then any and all rights granted directly or indirectly to You by such Participant, the Initial Developer (if the Initial Developer is not the Participant) and all Contributors under Sections 2.1 and/or 2.2 of this License shall, upon 60 days notice from Participant terminate prospectively and automatically at the expiration of such 60 day notice period, unless if within such 60 day period You withdraw Your claim with respect to the Participant Software against such Participant either unilaterally or pursuant to a written agreement with Participant.
442 |
443 | 6.3. In the event of termination under Sections 6.1 or 6.2 above, all end user licenses that have been validly granted by You or any distributor hereunder prior to termination (excluding licenses granted to You by any distributor) shall survive termination.
444 |
445 | 7. LIMITATION OF LIABILITY.
446 |
447 | UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED SOFTWARE, OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOST PROFITS, LOSS OF GOODWILL, WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY RESULTING FROM SUCH PARTY.S NEGLIGENCE TO THE EXTENT APPLICABLE LAW PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU.
448 |
449 | 8. U.S. GOVERNMENT END USERS.
450 |
451 | The Covered Software is a .commercial item,. as that term is defined in 48 C.F.R. 2.101 (Oct. 1995), consisting of .commercial computer software. (as that term is defined at 48 C.F.R. ? 252.227-7014(a)(1)) and .commercial computer software documentation. as such terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), all U.S. Government End Users acquire Covered Software with only those rights set forth herein. This U.S. Government Rights clause is in lieu of, and supersedes, any other FAR, DFAR, or other clause or provision that addresses Government rights in computer software under this License.
452 |
453 | 9. MISCELLANEOUS.
454 |
455 | This License represents the complete agreement concerning subject matter hereof. If any provision of this License is held to be unenforceable, such provision shall be reformed only to the extent necessary to make it enforceable. This License shall be governed by the law of the jurisdiction specified in a notice contained within the Original Software (except to the extent applicable law, if any, provides otherwise), excluding such jurisdiction.s conflict-of-law provisions. Any litigation relating to this License shall be subject to the jurisdiction of the courts located in the jurisdiction and venue specified in a notice contained within the Original Software, with the losing party responsible for costs, including, without limitation, court costs and reasonable attorneys. fees and expenses. The application of the United Nations Convention on Contracts for the International Sale of Goods is expressly excluded. Any law or regulation which provides that the language of a contract shall be construed against the drafter shall not apply to this License. You agree that You alone are responsible for compliance with the United States export administration regulations (and the export control laws and regulation of any other countries) when You use, distribute or otherwise make available any Covered Software.
456 |
457 | 10. RESPONSIBILITY FOR CLAIMS.
458 |
459 | As between Initial Developer and the Contributors, each party is responsible for claims and damages arising, directly or indirectly, out of its utilization of rights under this License and You agree to work with Initial Developer and Contributors to distribute such responsibility on an equitable basis. Nothing herein is intended or shall be deemed to constitute any admission of liability.
460 |
461 | NOTICE PURSUANT TO SECTION 9 OF THE COMMON DEVELOPMENT AND DISTRIBUTION LICENSE (CDDL)
462 |
463 | The code released under the CDDL shall be governed by the laws of the State of California (excluding conflict-of-law provisions). Any litigation relating to this License shall be subject to the jurisdiction of the Federal Courts of the Northern District of California and the state courts of the State of California, with venue lying in Santa Clara County, California.
464 |
465 |
466 | ### EPL v. 1
467 |
468 |
469 | Eclipse Public License - v 1.0
470 |
471 | THE ACCOMPANYING PROGRAM IS PROVIDED UNDER THE TERMS OF THIS ECLIPSE PUBLIC
472 | LICENSE ("AGREEMENT"). ANY USE, REPRODUCTION OR DISTRIBUTION OF THE PROGRAM
473 | CONSTITUTES RECIPIENT'S ACCEPTANCE OF THIS AGREEMENT.
474 |
475 | 1. DEFINITIONS
476 |
477 | "Contribution" means:
478 |
479 | a) in the case of the initial Contributor, the initial code and
480 | documentation distributed under this Agreement, and
481 | b) in the case of each subsequent Contributor:
482 |
483 | i) changes to the Program, and
484 |
485 | ii) additions to the Program;
486 |
487 | where such changes and/or additions to the Program originate from and are
488 | distributed by that particular Contributor. A Contribution 'originates' from a
489 | Contributor if it was added to the Program by such Contributor itself or anyone
490 | acting on such Contributor's behalf. Contributions do not include additions to
491 | the Program which: (i) are separate modules of software distributed in
492 | conjunction with the Program under their own license agreement, and (ii) are
493 | not derivative works of the Program.
494 |
495 | "Contributor" means any person or entity that distributes the Program.
496 |
497 | "Licensed Patents " mean patent claims licensable by a Contributor which are
498 | necessarily infringed by the use or sale of its Contribution alone or when
499 | combined with the Program.
500 |
501 | "Program" means the Contributions distributed in accordance with this Agreement.
502 |
503 | "Recipient" means anyone who receives the Program under this Agreement,
504 | including all Contributors.
505 |
506 | 2. GRANT OF RIGHTS
507 |
508 | a) Subject to the terms of this Agreement, each Contributor hereby grants
509 | Recipient a non-exclusive, worldwide, royalty-free copyright license to
510 | reproduce, prepare derivative works of, publicly display, publicly perform,
511 | distribute and sublicense the Contribution of such Contributor, if any, and
512 | such derivative works, in source code and object code form.
513 |
514 | b) Subject to the terms of this Agreement, each Contributor hereby grants
515 | Recipient a non-exclusive, worldwide, royalty-free patent license under
516 | Licensed Patents to make, use, sell, offer to sell, import and otherwise
517 | transfer the Contribution of such Contributor, if any, in source code and
518 | object code form. This patent license shall apply to the combination of the
519 | Contribution and the Program if, at the time the Contribution is added by the
520 | Contributor, such addition of the Contribution causes such combination to be
521 | covered by the Licensed Patents. The patent license shall not apply to any
522 | other combinations which include the Contribution. No hardware per se is
523 | licensed hereunder.
524 |
525 | c) Recipient understands that although each Contributor grants the
526 | licenses to its Contributions set forth herein, no assurances are provided by
527 | any Contributor that the Program does not infringe the patent or other
528 | intellectual property rights of any other entity. Each Contributor disclaims
529 | any liability to Recipient for claims brought by any other entity based on
530 | infringement of intellectual property rights or otherwise. As a condition to
531 | exercising the rights and licenses granted hereunder, each Recipient hereby
532 | assumes sole responsibility to secure any other intellectual property rights
533 | needed, if any. For example, if a third party patent license is required to
534 | allow Recipient to distribute the Program, it is Recipient's responsibility to
535 | acquire that license before distributing the Program.
536 |
537 | d) Each Contributor represents that to its knowledge it has sufficient
538 | copyright rights in its Contribution, if any, to grant the copyright license
539 | set forth in this Agreement.
540 |
541 | 3. REQUIREMENTS
542 |
543 | A Contributor may choose to distribute the Program in object code form under
544 | its own license agreement, provided that:
545 |
546 | a) it complies with the terms and conditions of this Agreement; and
547 |
548 | b) its license agreement:
549 |
550 | i) effectively disclaims on behalf of all Contributors all warranties and
551 | conditions, express and implied, including warranties or conditions of title
552 | and non-infringement, and implied warranties or conditions of merchantability
553 | and fitness for a particular purpose;
554 |
555 | ii) effectively excludes on behalf of all Contributors all liability for
556 | damages, including direct, indirect, special, incidental and consequential
557 | damages, such as lost profits;
558 |
559 | iii) states that any provisions which differ from this Agreement are
560 | offered by that Contributor alone and not by any other party; and
561 |
562 | iv) states that source code for the Program is available from such
563 | Contributor, and informs licensees how to obtain it in a reasonable manner on
564 | or through a medium customarily used for software exchange.
565 |
566 | When the Program is made available in source code form:
567 |
568 | a) it must be made available under this Agreement; and
569 |
570 | b) a copy of this Agreement must be included with each copy of the
571 | Program.
572 |
573 | Contributors may not remove or alter any copyright notices contained within the
574 | Program.
575 |
576 | Each Contributor must identify itself as the originator of its Contribution, if
577 | any, in a manner that reasonably allows subsequent Recipients to identify the
578 | originator of the Contribution.
579 |
580 | 4. COMMERCIAL DISTRIBUTION
581 |
582 | Commercial distributors of software may accept certain responsibilities with
583 | respect to end users, business partners and the like. While this license is
584 | intended to facilitate the commercial use of the Program, the Contributor who
585 | includes the Program in a commercial product offering should do so in a manner
586 | which does not create potential liability for other Contributors. Therefore, if
587 | a Contributor includes the Program in a commercial product offering, such
588 | Contributor ("Commercial Contributor") hereby agrees to defend and indemnify
589 | every other Contributor ("Indemnified Contributor") against any losses, damages
590 | and costs (collectively "Losses") arising from claims, lawsuits and other legal
591 | actions brought by a third party against the Indemnified Contributor to the
592 | extent caused by the acts or omissions of such Commercial Contributor in
593 | connection with its distribution of the Program in a commercial product
594 | offering. The obligations in this section do not apply to any claims or Losses
595 | relating to any actual or alleged intellectual property infringement. In order
596 | to qualify, an Indemnified Contributor must: a) promptly notify the Commercial
597 | Contributor in writing of such claim, and b) allow the Commercial Contributor
598 | to control, and cooperate with the Commercial Contributor in, the defense and
599 | any related settlement negotiations. The Indemnified Contributor may
600 | participate in any such claim at its own expense.
601 |
602 | For example, a Contributor might include the Program in a commercial product
603 | offering, Product X. That Contributor is then a Commercial Contributor. If that
604 | Commercial Contributor then makes performance claims, or offers warranties
605 | related to Product X, those performance claims and warranties are such
606 | Commercial Contributor's responsibility alone. Under this section, the
607 | Commercial Contributor would have to defend claims against the other
608 | Contributors related to those performance claims and warranties, and if a court
609 | requires any other Contributor to pay any damages as a result, the Commercial
610 | Contributor must pay those damages.
611 |
612 | 5. NO WARRANTY
613 |
614 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, THE PROGRAM IS PROVIDED ON AN
615 | "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, EITHER EXPRESS OR
616 | IMPLIED INCLUDING, WITHOUT LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE,
617 | NON-INFRINGEMENT, MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each
618 | Recipient is solely responsible for determining the appropriateness of using
619 | and distributing the Program and assumes all risks associated with its exercise
620 | of rights under this Agreement, including but not limited to the risks and
621 | costs of program errors, compliance with applicable laws, damage to or loss of
622 | data, programs or equipment, and unavailability or interruption of operations.
623 |
624 | 6. DISCLAIMER OF LIABILITY
625 |
626 | EXCEPT AS EXPRESSLY SET FORTH IN THIS AGREEMENT, NEITHER RECIPIENT NOR ANY
627 | CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY DIRECT, INDIRECT, INCIDENTAL,
628 | SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING WITHOUT LIMITATION LOST
629 | PROFITS), HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
630 | STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY
631 | WAY OUT OF THE USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS
632 | GRANTED HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
633 |
634 | 7. GENERAL
635 |
636 | If any provision of this Agreement is invalid or unenforceable under applicable
637 | law, it shall not affect the validity or enforceability of the remainder of the
638 | terms of this Agreement, and without further action by the parties hereto, such
639 | provision shall be reformed to the minimum extent necessary to make such
640 | provision valid and enforceable.
641 |
642 | If Recipient institutes patent litigation against any
643 | entity (including a cross-claim or counterclaim in a lawsuit) alleging that the
644 | Program itself (excluding combinations of the Program with other software or
645 | hardware) infringes such Recipient's patent(s), then such Recipient's rights
646 | granted under Section 2(b) shall terminate as of the date such litigation is
647 | filed.
648 |
649 | All Recipient's rights under this Agreement shall terminate if it fails to
650 | comply with any of the material terms or conditions of this Agreement and does
651 | not cure such failure in a reasonable period of time after becoming aware of
652 | such noncompliance. If all Recipient's rights under this Agreement terminate,
653 | Recipient agrees to cease use and distribution of the Program as soon as
654 | reasonably practicable. However, Recipient's obligations under this Agreement
655 | and any licenses granted by Recipient relating to the Program shall continue
656 | and survive.
657 |
658 | Everyone is permitted to copy and distribute copies of this Agreement, but in
659 | order to avoid inconsistency the Agreement is copyrighted and may only be
660 | modified in the following manner. The Agreement Steward reserves the right to
661 | publish new versions (including revisions) of this Agreement from time to time.
662 | No one other than the Agreement Steward has the right to modify this Agreement.
663 | The Eclipse Foundation is the initial Agreement Steward. The Eclipse Foundation may assign the responsibility to
664 | serve as the Agreement Steward to a suitable separate entity. Each new version
665 | of the Agreement will be given a distinguishing version number. The Program
666 | (including Contributions) may always be distributed subject to the version of
667 | the Agreement under which it was received. In addition, after a new version of
668 | the Agreement is published, Contributor may elect to distribute the Program
669 | (including its Contributions) under the new version. Except as expressly stated
670 | in Sections 2(a) and 2(b) above, Recipient receives no rights or licenses to
671 | the intellectual property of any Contributor under this Agreement, whether
672 | expressly, by implication, estoppel or otherwise. All rights in the Program not
673 | expressly granted under this Agreement are reserved.
674 |
675 | This Agreement is governed by the laws of the State of New York and the
676 | intellectual property laws of the United States of America. No party to this
677 | Agreement will bring a legal action under this Agreement more than one year
678 | after the cause of action arose. Each party waives its rights to a jury trial
679 | in any resulting litigation.
680 |
681 |
682 | ### MIT
683 |
684 |
685 | Permission is hereby granted, free of charge, to any person obtaining
686 | a copy of this software and associated documentation files (the
687 | "Software"), to deal in the Software without restriction, including
688 | without limitation the rights to use, copy, modify, merge, publish,
689 | distribute, sublicense, and/or sell copies of the Software, and to
690 | permit persons to whom the Software is furnished to do so, subject to
691 | the following conditions:
692 |
693 | The above copyright notice and this permission notice shall be
694 | included in all copies or substantial portions of the Software.
695 |
696 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
697 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
698 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
699 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
700 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
701 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
702 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
703 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
16 |
19 | 4.0.0
20 |
21 | io.curity
22 | oauth-filter
23 | 4.0.0
24 | OAuth API Filter
25 | A Servlet Filter that authenticates and authorizes requests using OAuth access tokens of various kinds.
26 | https://github.com/curityio/oauth-filter-for-java
27 |
28 |
29 | Curity AB
30 | https://curity.io
31 |
32 |
33 |
34 |
35 | The Apache License, Version 2.0
36 | https://www.apache.org/licenses/LICENSE-2.0.txt
37 |
38 |
39 |
40 |
41 |
42 | Travis Spencer
43 | travis.spencer@curity.io
44 | Curity AB
45 | https://curity.io
46 |
47 |
48 | Michal Trojanowski
49 | michal.trojanowski@curity.io
50 | Curity AB
51 | https://curity.io
52 |
53 |
54 | Judith Kahrer
55 | judith.kahrer@curity.io
56 | Curity AB
57 | https://curity.io
58 |
59 |
60 |
61 |
62 | scm:git:git://github.com:curityio/oauth-filter-for-java.git
63 | scm:git:ssh://github.com:curityio/oauth-filter-for-java.git
64 | https://github.com/curityio/oauth-filter-for-java
65 |
66 |
67 |
68 | UTF-8
69 |
70 |
71 |
72 |
73 |
74 | org.apache.maven.plugins
75 | maven-compiler-plugin
76 | 3.1
77 |
78 | 17
79 | 17
80 | true
81 | false
82 |
83 |
84 |
85 |
86 |
87 | maven-failsafe-plugin
88 | 2.22.2
89 |
90 |
91 | org.apache.logging.log4j.jul.LogManager
92 |
93 |
94 |
95 | **/integration/**/IntegrationTest*.java
96 | **/integration/**/*IntegrationTest.java
97 |
98 | UTF-8
99 |
100 |
101 |
102 |
103 | integration-test
104 | verify
105 |
106 |
107 |
108 |
109 |
110 |
111 | org.apache.maven.plugins
112 | maven-surefire-plugin
113 | 2.16
114 |
115 |
116 | org.apache.logging.log4j.jul.LogManager
117 |
118 |
119 |
120 | **/integration/**/*.java
121 |
122 |
123 |
124 |
125 |
126 |
127 |
128 |
129 |
130 | jakarta.servlet
131 | jakarta.servlet-api
132 | 6.0.0
133 |
134 |
135 | javax.json
136 | javax.json-api
137 |
138 |
139 | org.apache.httpcomponents
140 | httpclient
141 | true
142 |
143 |
144 |
145 |
146 |
147 | com.owlike
148 | genson
149 | test
150 |
151 |
152 | junit
153 | junit
154 | test
155 |
156 |
157 | org.mockito
158 | mockito-core
159 | test
160 |
161 |
162 | org.apache.logging.log4j
163 | log4j-core
164 | test
165 |
166 |
167 | org.apache.logging.log4j
168 | log4j-jul
169 | test
170 |
171 |
172 | org.apache.logging.log4j
173 | log4j-jcl
174 | test
175 |
176 |
177 | org.bitbucket.b_c
178 | jose4j
179 | test
180 |
181 |
182 |
183 |
184 |
185 |
186 | org.apache.httpcomponents
187 | httpclient
188 | 4.5.13
189 |
190 |
191 | javax.servlet
192 | javax.servlet-api
193 | 4.0.1
194 |
195 |
196 | com.google.guava
197 | guava
198 | 32.0.0-jre
199 |
200 |
201 | javax.json
202 | javax.json-api
203 | 1.1.4
204 |
205 |
206 | com.owlike
207 | genson
208 | 1.6
209 |
210 |
211 | org.bitbucket.b_c
212 | jose4j
213 | 0.9.4
214 |
215 |
216 | junit
217 | junit
218 | 4.13.2
219 |
220 |
221 | org.mockito
222 | mockito-core
223 | 3.10.0
224 | test
225 |
226 |
227 | org.apache.logging.log4j
228 | log4j-bom
229 | 2.14.1
230 | import
231 | pom
232 |
233 |
234 |
235 |
236 |
237 |
238 | release
239 |
240 |
241 | ossrh
242 | https://oss.sonatype.org/content/repositories/snapshots
243 |
244 |
245 | ossrh
246 | https://oss.sonatype.org/service/local/staging/deploy/maven2/
247 |
248 |
249 |
250 |
251 |
252 | org.apache.maven.plugins
253 | maven-source-plugin
254 | 3.2.1
255 |
256 |
257 | attach-sources
258 |
259 | jar-no-fork
260 |
261 |
262 |
263 |
264 |
265 | org.apache.maven.plugins
266 | maven-javadoc-plugin
267 | 3.3.0
268 |
269 |
270 | attach-javadocs
271 |
272 | jar
273 |
274 |
275 |
276 |
277 | 8
278 |
279 |
280 |
281 | org.apache.maven.plugins
282 | maven-gpg-plugin
283 | 3.0.1
284 |
285 |
286 | sign-artifacts
287 | verify
288 |
289 | sign
290 |
291 |
292 |
293 |
294 |
295 | org.sonatype.plugins
296 | nexus-staging-maven-plugin
297 | 1.6.8
298 | true
299 |
300 | ossrh
301 | https://oss.sonatype.org/
302 | true
303 |
304 |
305 |
306 |
307 |
308 |
309 |
310 |
--------------------------------------------------------------------------------
/readme.md:
--------------------------------------------------------------------------------
1 | # OAuth Filter for Java
2 |
3 | [](https://curity.io/resources/code-examples/status/)
4 | [](https://curity.io/resources/code-examples/status/)
5 |
6 | This project contains a Servlet Filter that authenticates and authorizes requests using OAuth access tokens of various kinds. There are two `OAuthFilter` implementations. `OAuthJwtFilter` and `OAuthOpaqueFilter`. Both implement `jakarta.servlet.Filter`, and can be used to protect APIs built using Java. Depending on the format of the access token, these two concrete implementations can be used in the following manner:
7 |
8 | 1. If the token is a Json Web Token (JWT) then validate the token using a public key
9 | 2. If the token is a reference (opaque) token, then validate by calling the OAuth server's
10 | [introspection](https://tools.ietf.org/search/rfc7662) endpoint.
11 |
12 | An example of how to use this filter can be found in a [separate repository](https://github.com/curityio/example-java-oauth-protected-api).
13 |
14 | ## Filter Overview
15 |
16 | The filter is build to perform two tasks.
17 |
18 | 1. Authenticate the caller by validating the incoming access token
19 | 2. Authorize the operation by validating the scopes in the access token against the configured scopes
20 |
21 | The authorization is very basic, and in the default implementation only checks that all configured scopes are present in the token. A more advanced scenario could check the HTTP method, along with sub-paths in order to determine if the appropriate scope is present in the request. To change the default behavior, override the method `io.curity.oauth.OAuthFilter#authorize`.
22 |
23 | ## Using Json Web Tokens (JWT)
24 |
25 | `OAuthJwtFilter` implements a filter that expects a Json Web Token, and that can validate the token either by using a pre-shared certificate or by calling the OAuth servers Json Web Key (JWK) endpoint. The default is to use the JWK service, as this provides a more maintainable deployment structure for microservices.
26 |
27 | ## Using Opaque Tokens
28 |
29 | `OAuthOpaqueFilter` implements a filter that expects an opaque token (i.e., a token that needs to be introspected in order to determine the contents). This requires the OAuth server to support [introspection](https://tools.ietf.org/search/rfc7662). Introspection means that the API acts as an introspecting client, and, therefore, needs client credentials in order to authenticate itself against the introspection endpoint. Each new token received is introspected, then cached for a limited time. In production, this should be refined to perhaps use a shared cache or at least a datastore for the cache if there is a large number of requests coming in to the API.
30 |
31 | ## Scope-bases Authorization
32 |
33 | The abstract class `OAuthFilter` implements a simple authorize method, that validates the incoming scopes against the configured ones. It is simple to override this method in the implementing classes instead to perform more advanced authorization.
34 |
35 | ## Installing the library
36 |
37 | The `oauth-filter` library is available on Maven Central since version 3.0.0. so you can easily include in your projects.
38 |
39 | For example, if you use Maven, add to your `pom.xml`:
40 |
41 | ```xml
42 |
43 | io.curity
44 | oauth-filter
45 | 4.0.0
46 |
47 | ```
48 |
49 | or with Gradle, inlcude in `build.gradle`:
50 |
51 | ```groovy
52 | implementation 'io.curity:oauth-filter:4.0.0'
53 | ```
54 |
55 | ## Configuring the Filter
56 |
57 | To configure the filter, the following settings are required for each of the two concrete implementations, depending on the format of the token your OAuth server is using.
58 |
59 | ### Init-params for the `OAuthJwtFilter`
60 |
61 | Configuration Setting Name | Description
62 | ---------------------------|----------------
63 | oauthHost | Hostname of the OAuth server.
64 | oauthPort | Port of the OAuth server.
65 | jsonWebKeysPath | Path to the JWKS endpoint on the OAuth server.
66 | scope | A space separated list of scopes required to access the API.
67 | minKidReloadTimeInSeconds | Minimum time to reload the webKeys cache used by the Filter.
68 |
69 | ### Init-params for the `OAuthOpaqueFilter`
70 |
71 | Configuration Setting Name | Description
72 | ---------------------------|----------------
73 | oauthHost | Hostname of the OAuth server.
74 | oauthPort | Port of the OAuth server.
75 | introspectionPath | Path to the introspection endpoint on the OAuth server.
76 | scope | A space separated list of scopes required to access the API.
77 | clientId | Your application's client id to use for introspection.
78 | clientSecret | Your application's client secret.
79 |
80 | ## Providing an external HttpClient
81 |
82 | The `OAuthFilter` uses an [HttpClient](https://hc.apache.org/httpcomponents-client-ga/) to communicate with the authentication server. The HttpClient may be overridden by the web application by defining a service Provider class in this location:
83 |
84 | * `META-INF/services/io.curity.oauth.HttpClientProvider` relative to the classpath
85 |
86 | The file should contain the name of the class used as the provider, e.g. `com.example.HttpClientProvider`
87 |
88 | This class must extend the abstract class `io.curity.oauth.HttpClientProvider` and implement two methods which create an introspection client (implementation of `io.curity.oauth.IntrospectionClient`), and a web keys client (implementation of `io.curity.oauth.WebKeysClient`).
89 |
90 | ## More Information
91 |
92 | For more information, please contact [Curity](http://curity.io).
93 |
94 | Copyright (C) 2016-2022 Curity AB. All rights reserved
95 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/AbstractJwtValidator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import javax.json.JsonObject;
20 | import javax.json.JsonReader;
21 | import javax.json.JsonReaderFactory;
22 | import java.io.StringReader;
23 | import java.nio.ByteBuffer;
24 | import java.nio.charset.StandardCharsets;
25 | import java.security.PublicKey;
26 | import java.security.Signature;
27 | import java.security.interfaces.EdECPublicKey;
28 | import java.time.Instant;
29 | import java.util.Base64;
30 | import java.util.HashMap;
31 | import java.util.Map;
32 | import java.util.Optional;
33 | import java.util.logging.Level;
34 | import java.util.logging.Logger;
35 |
36 | abstract class AbstractJwtValidator implements JwtValidator
37 | {
38 | private static final Logger _logger = Logger.getLogger(AbstractJwtValidator.class.getName());
39 |
40 | // Caches with object scope that will ensure that we only decode the same JWT parts once per the lifetime of this
41 | // object
42 | private final Map _decodedJwtBodyByEncodedBody = new HashMap<>(1);
43 | private final Map _decodedJwtHeaderByEncodedHeader = new HashMap<>(1);
44 | private final JsonReaderFactory _jsonReaderFactory;
45 | private final String _audience;
46 | private final String _issuer;
47 |
48 | AbstractJwtValidator(String issuer, String audience, JsonReaderFactory jsonReaderFactory)
49 | {
50 | _issuer = issuer;
51 | _audience = audience;
52 | _jsonReaderFactory = jsonReaderFactory;
53 | }
54 |
55 | public final JsonData validate(String jwt) throws TokenValidationException
56 | {
57 | String[] jwtParts = jwt.split("\\.");
58 |
59 | if (jwtParts.length != 3)
60 | {
61 | throw new InvalidTokenFormatException();
62 | }
63 |
64 | JsonObject jwtBody = decodeJwtBody(jwtParts[1]);
65 | JwtHeader jwtHeader = decodeJwtHeader(jwtParts[0]);
66 | byte[] jwtSignature = Base64.getUrlDecoder().decode(jwtParts[2]);
67 | byte[] headerAndPayload = convertToBytes(jwtParts[0] + "." + jwtParts[1]);
68 |
69 | validateSignature(jwtHeader, jwtSignature, headerAndPayload);
70 |
71 | try
72 | {
73 | long exp = JsonUtils.getLong(jwtBody, "exp");
74 | long iat = JsonUtils.getLong(jwtBody, "iat");
75 |
76 | String aud = JsonUtils.getString(jwtBody, "aud");
77 | String iss = JsonUtils.getString(jwtBody, "iss");
78 |
79 | assert aud != null && aud.length() > 0 : "aud claim is not present in JWT";
80 | assert iss != null && iss.length() > 0 : "iss claim is not present in JWT";
81 |
82 | if (!aud.equals(_audience))
83 | {
84 | throw new InvalidAudienceException(_audience, aud);
85 | }
86 |
87 | if (!iss.equals(_issuer))
88 | {
89 | throw new InvalidIssuerException(_issuer, iss);
90 | }
91 |
92 | Instant now = Instant.now();
93 |
94 | if (now.getEpochSecond() > exp)
95 | {
96 | throw new ExpiredTokenException();
97 | }
98 |
99 | if (now.getEpochSecond() < iat)
100 | {
101 | throw new InvalidIssuanceInstantException();
102 | }
103 | }
104 | catch (Exception e)
105 | {
106 | _logger.log(Level.INFO, "Could not extract token data", e);
107 |
108 | throw new InvalidTokenFormatException("Failed to extract data from Token");
109 | }
110 |
111 | return new JsonData(jwtBody);
112 | }
113 |
114 | private void validateSignature(JwtHeader jwtHeader, byte[] jwtSignatureData,
115 | byte[] headerAndPayload)
116 | throws TokenValidationException
117 | {
118 | String algorithm = jwtHeader.getAlgorithm();
119 |
120 | if (algorithm == null || algorithm.length() <= 0)
121 | {
122 | throw new MissingAlgorithmException();
123 | }
124 |
125 | if (canRecognizeAlg(algorithm))
126 | {
127 | Optional signatureVerificationKey = getPublicKey(jwtHeader);
128 |
129 | if (signatureVerificationKey.isEmpty())
130 | {
131 | _logger.warning("Received token but could not find matching key");
132 |
133 | throw new UnknownSignatureVerificationKey();
134 | }
135 |
136 | if (!verifySignature(algorithm, headerAndPayload, jwtSignatureData, signatureVerificationKey.get()))
137 | {
138 | throw new InvalidSignatureException();
139 | }
140 | }
141 | else
142 | {
143 | _logger.warning(() -> String.format("Requested JsonWebKey using unrecognizable alg: %s", algorithm));
144 |
145 | throw new UnknownAlgorithmException(algorithm);
146 | }
147 | }
148 |
149 | protected abstract Optional getPublicKey(JwtHeader jwtHeader);
150 |
151 | /**
152 | * Convert base64 to bytes (ASCII)
153 | *
154 | * @param input input
155 | * @return The array of bytes
156 | */
157 | private byte[] convertToBytes(String input)
158 | {
159 | byte[] bytes = new byte[input.length()];
160 |
161 | for (int i = 0; i < input.length(); i++)
162 | {
163 | //Convert and treat as ascii.
164 | int integer = input.charAt(i);
165 |
166 | //Since byte is signed in Java we cannot use normal conversion
167 | //but must drop it into a byte array and truncate.
168 | byte[] rawBytes = ByteBuffer.allocate(4).putInt(integer).array();
169 | //Only store the least significant byte (the others should be 0 TODO check)
170 | bytes[i] = rawBytes[3];
171 | }
172 |
173 | return bytes;
174 | }
175 |
176 | private boolean verifySignature(String algorithm, byte[] signingInput, byte[] signature, PublicKey publicKey)
177 | {
178 | try
179 | {
180 | Signature verifier = switch (algorithm) {
181 | case "RS256" -> Signature.getInstance("SHA256withRSA");
182 | case "EdDSA" -> Signature.getInstance(((EdECPublicKey) publicKey).getParams().getName());
183 | default -> throw new UnknownAlgorithmException(String.format("Unsupported signature algorithm '%s'", algorithm));
184 | };
185 |
186 | verifier.initVerify(publicKey);
187 | verifier.update(signingInput);
188 |
189 | return verifier.verify(signature);
190 | }
191 | catch (Exception e)
192 | {
193 | throw new RuntimeException("Unable to validate JWT signature", e);
194 | }
195 | }
196 |
197 | private boolean canRecognizeAlg(String alg)
198 | {
199 | return switch (alg) {
200 | case "RS256", "EdDSA" -> true;
201 | default -> false;
202 | };
203 | }
204 |
205 | private JsonObject decodeJwtBody(String body)
206 | {
207 | return _decodedJwtBodyByEncodedBody.computeIfAbsent(body, key ->
208 | {
209 | // TODO: Switch to stream
210 | String decodedBody = new String(Base64.getUrlDecoder().decode(body), StandardCharsets.UTF_8);
211 | JsonReader jsonBodyReader = _jsonReaderFactory.createReader(new StringReader(decodedBody));
212 |
213 | return jsonBodyReader.readObject();
214 | });
215 | }
216 |
217 | private JwtHeader decodeJwtHeader(String header)
218 | {
219 | return _decodedJwtHeaderByEncodedHeader.computeIfAbsent(header, key ->
220 | {
221 | Base64.Decoder base64 = Base64.getDecoder();
222 | String decodedHeader = new String(base64.decode(header), StandardCharsets.UTF_8);
223 | JsonReader jsonHeaderReader = _jsonReaderFactory.createReader(new StringReader(decodedHeader));
224 |
225 | return new JwtHeader(jsonHeaderReader.readObject());
226 | });
227 | }
228 |
229 | class JwtHeader
230 | {
231 | private final JsonObject _jsonObject;
232 |
233 | JwtHeader(JsonObject jsonObject)
234 | {
235 | _jsonObject = jsonObject;
236 | }
237 |
238 | String getAlgorithm()
239 | {
240 | return getString("alg");
241 | }
242 |
243 | String getKeyId()
244 | {
245 | return getString("kid");
246 | }
247 |
248 | String getString(String name)
249 | {
250 | return JsonUtils.getString(_jsonObject, name);
251 | }
252 | }
253 | }
254 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/AuthenticatedUser.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import javax.json.JsonObject;
20 | import javax.json.JsonValue;
21 | import java.util.Objects;
22 | import java.util.Set;
23 |
24 | public class AuthenticatedUser
25 | {
26 | private final String _sub;
27 | private final Set _scopes;
28 | private final JsonData _jsonData;
29 |
30 | private AuthenticatedUser(String subject, Set scopes, JsonData jsonData)
31 | {
32 | _sub = subject;
33 | _scopes = scopes;
34 | _jsonData = jsonData;
35 | }
36 |
37 | public String getSubject()
38 | {
39 | return _sub;
40 | }
41 |
42 | public Set getScopes()
43 | {
44 | return _scopes;
45 | }
46 |
47 | public JsonValue getClaim(String name)
48 | {
49 | return _jsonData.getClaim(name);
50 | }
51 |
52 | public JsonObject getClaims()
53 | {
54 | return _jsonData.getClaims();
55 | }
56 |
57 | static AuthenticatedUser from(JsonData tokenData)
58 | {
59 | Objects.requireNonNull(tokenData);
60 |
61 | String subject = tokenData.getSubject();
62 |
63 | Objects.requireNonNull(subject);
64 |
65 | return new AuthenticatedUser(subject, tokenData.getScopes(), tokenData);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/AuthenticatedUserRequestWrapper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import jakarta.servlet.ServletException;
20 | import jakarta.servlet.http.HttpServletRequest;
21 | import jakarta.servlet.http.HttpServletRequestWrapper;
22 | import jakarta.servlet.http.HttpServletResponse;
23 | import java.io.IOException;
24 | import java.security.Principal;
25 |
26 | class AuthenticatedUserRequestWrapper extends HttpServletRequestWrapper
27 | {
28 | /**
29 | * String identifier for OAuth authentication (i.e., authentication that conforms to RFC 6750). Value "OAUTH".
30 | *
31 | * @see RFC 6750 - The OAuth 2.0 Authorization Framework: Bearer
32 | * Token Usage
33 | */
34 | @SuppressWarnings("WeakerAccess")
35 | public static final String OAUTH_AUTH = "OAUTH";
36 |
37 | private final HttpServletRequest _request;
38 |
39 | private AuthenticatedUser _authenticatedUser; // Not final because logout mutates this
40 |
41 | AuthenticatedUserRequestWrapper(HttpServletRequest request, AuthenticatedUser authenticatedUser)
42 | {
43 | super(request);
44 |
45 | _request = request;
46 | _authenticatedUser = authenticatedUser;
47 | }
48 |
49 | @Override
50 | public String getRemoteUser()
51 | {
52 | return _authenticatedUser == null ? _request.getRemoteUser() : _authenticatedUser.getSubject();
53 | }
54 |
55 | @Override
56 | public Principal getUserPrincipal()
57 | {
58 | return _authenticatedUser == null ? _request.getUserPrincipal() : _authenticatedUser::getSubject;
59 | }
60 |
61 | @Override
62 | public String getAuthType()
63 | {
64 | //noinspection VariableNotUsedInsideIf
65 | return _authenticatedUser == null ? _request.getAuthType() : OAUTH_AUTH;
66 | }
67 |
68 | @Override
69 | public boolean authenticate(HttpServletResponse response) throws IOException, ServletException
70 | {
71 | return _authenticatedUser != null || (response.isCommitted() &&
72 | (response.getStatus() == HttpServletResponse.SC_UNAUTHORIZED ||
73 | response.getStatus() == HttpServletResponse.SC_FORBIDDEN));
74 | }
75 |
76 | @Override
77 | public void login(String username, String password) throws ServletException
78 | {
79 | throw new ServletException("Authentication with username/password is not supported");
80 | }
81 |
82 | @Override
83 | public void logout() throws ServletException
84 | {
85 | _authenticatedUser = null;
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/DefaultHttpClientProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import org.apache.http.client.HttpClient;
20 | import org.apache.http.impl.client.HttpClients;
21 |
22 | import jakarta.servlet.UnavailableException;
23 | import java.net.URI;
24 | import java.net.URISyntaxException;
25 | import java.util.Map;
26 | import java.util.concurrent.TimeUnit;
27 | import java.util.logging.Level;
28 | import java.util.logging.Logger;
29 |
30 | import static io.curity.oauth.FilterHelper.getInitParamValue;
31 |
32 | class DefaultHttpClientProvider extends HttpClientProvider
33 | {
34 | private static final Logger _logger = Logger.getLogger(OAuthOpaqueFilter.class.getName());
35 |
36 | private interface InitParams
37 | {
38 | String OAUTH_HOST = "oauthHost";
39 | String OAUTH_PORT = "oauthPort";
40 | String JSON_WEB_KEYS_PATH = "jsonWebKeysPath";
41 | String INTROSPECTION_PATH = "introspectionPath";
42 | String CLIENT_ID = "clientId";
43 | String CLIENT_SECRET = "clientSecret";
44 | }
45 |
46 | @Override
47 | public IntrospectionClient createIntrospectionClient(Map config) throws UnavailableException
48 | {
49 | String oauthHost = getInitParamValue(InitParams.OAUTH_HOST, config);
50 | int oauthPort = getInitParamValue(InitParams.OAUTH_PORT, config, Integer::parseInt);
51 |
52 | String introspectionPath = getInitParamValue(InitParams.INTROSPECTION_PATH, config);
53 | String clientId = getInitParamValue(InitParams.CLIENT_ID, config);
54 | String clientSecret = getInitParamValue(InitParams.CLIENT_SECRET, config);
55 |
56 | URI introspectionUri = null;
57 | try
58 | {
59 | introspectionUri = new URI("https", null, oauthHost, oauthPort, introspectionPath, null, null);
60 | }
61 | catch (URISyntaxException e)
62 | {
63 | _logger.log(Level.SEVERE, "Invalid parameters", e);
64 |
65 | throw new UnavailableException("Service is unavailable");
66 | }
67 |
68 | HttpClient httpClient = HttpClients
69 | .custom()
70 | .disableAuthCaching()
71 | .disableAutomaticRetries()
72 | .disableRedirectHandling()
73 | .setConnectionTimeToLive(2, TimeUnit.SECONDS)
74 | .build();
75 |
76 | return new DefaultIntrospectClient(introspectionUri, clientId, clientSecret, httpClient);
77 | }
78 |
79 | @Override
80 | public WebKeysClient createWebKeysClient(Map config) throws UnavailableException
81 | {
82 | URI webKeysUri;
83 |
84 | try
85 | {
86 | int oauthPort = FilterHelper.getInitParamValue(InitParams.OAUTH_PORT, config, Integer::parseInt);
87 | String webKeysPath = FilterHelper.getInitParamValue(InitParams.JSON_WEB_KEYS_PATH, config);
88 | String oauthHost = FilterHelper.getInitParamValue(InitParams.OAUTH_HOST, config);
89 |
90 | webKeysUri = new URI("https", null, oauthHost, oauthPort, webKeysPath, null, null);
91 | }
92 | catch (URISyntaxException e)
93 | {
94 | _logger.log(Level.SEVERE, "Invalid parameters", e);
95 |
96 | throw new UnavailableException("Service is unavailable");
97 | }
98 |
99 | HttpClient httpClient = HttpClients
100 | .custom()
101 | .disableAutomaticRetries()
102 | .disableRedirectHandling()
103 | .setConnectionTimeToLive(2, TimeUnit.SECONDS)
104 | .build();
105 |
106 | return new DefaultWebKeysClient(webKeysUri, httpClient);
107 | }
108 | }
109 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/DefaultIntrospectClient.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import org.apache.http.HttpResponse;
20 | import org.apache.http.HttpStatus;
21 | import org.apache.http.NameValuePair;
22 | import org.apache.http.client.HttpClient;
23 | import org.apache.http.client.entity.UrlEncodedFormEntity;
24 | import org.apache.http.client.methods.HttpPost;
25 | import org.apache.http.entity.ContentType;
26 | import org.apache.http.message.BasicNameValuePair;
27 | import org.apache.http.util.EntityUtils;
28 |
29 | import java.io.Closeable;
30 | import java.io.IOException;
31 | import java.net.URI;
32 | import java.nio.charset.StandardCharsets;
33 | import java.util.ArrayList;
34 | import java.util.List;
35 | import java.util.logging.Logger;
36 |
37 | import static org.apache.http.HttpHeaders.ACCEPT;
38 |
39 | class DefaultIntrospectClient implements IntrospectionClient
40 | {
41 | private static final Logger _logger = Logger.getLogger(DefaultIntrospectClient.class.getName());
42 |
43 | private final HttpClient _httpClient;
44 | private final URI _introspectionUri;
45 | private final String _clientId;
46 | private final String _clientSecret;
47 |
48 | DefaultIntrospectClient(URI introspectionUri, String clientId, String clientSecret, HttpClient httpClient)
49 | {
50 | _introspectionUri = introspectionUri;
51 | _clientId = clientId;
52 | _clientSecret = clientSecret;
53 | _httpClient = httpClient;
54 | }
55 |
56 | @Override
57 | public String introspect(String token) throws IOException
58 | {
59 | HttpPost post = new HttpPost(_introspectionUri);
60 |
61 | post.setHeader(ACCEPT, ContentType.APPLICATION_JSON.getMimeType());
62 |
63 | List params = new ArrayList<>(3);
64 |
65 | params.add(new BasicNameValuePair("token", token));
66 | params.add(new BasicNameValuePair("client_id", _clientId));
67 | params.add(new BasicNameValuePair("client_secret", _clientSecret));
68 |
69 | post.setEntity(new UrlEncodedFormEntity(params));
70 |
71 | HttpResponse response = _httpClient.execute(post);
72 |
73 | if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK)
74 | {
75 | _logger.severe(() -> "Got error from introspection server: " + response.getStatusLine().getStatusCode());
76 |
77 | throw new IOException("Got error from introspection server: " + response.getStatusLine().getStatusCode());
78 | }
79 |
80 | return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
81 | }
82 |
83 | @Override
84 | public void close() throws IOException
85 | {
86 | if (_httpClient instanceof Closeable)
87 | {
88 | ((Closeable) _httpClient).close();
89 | }
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/DefaultWebKeysClient.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import org.apache.http.HttpResponse;
20 | import org.apache.http.HttpStatus;
21 | import org.apache.http.client.HttpClient;
22 | import org.apache.http.client.methods.HttpGet;
23 | import org.apache.http.entity.ContentType;
24 | import org.apache.http.util.EntityUtils;
25 |
26 | import java.io.IOException;
27 | import java.net.URI;
28 | import java.nio.charset.StandardCharsets;
29 | import java.util.logging.Logger;
30 |
31 | import static org.apache.http.HttpHeaders.ACCEPT;
32 |
33 | class DefaultWebKeysClient implements WebKeysClient
34 | {
35 | private static final Logger _logger = Logger.getLogger(DefaultWebKeysClient.class.getName());
36 | private final URI _jwksUri;
37 | private final HttpClient _httpClient;
38 |
39 | DefaultWebKeysClient(URI jwksUri, HttpClient httpClient)
40 | {
41 | _jwksUri = jwksUri;
42 | _httpClient = httpClient;
43 | }
44 |
45 | @Override
46 | public String getKeys() throws IOException
47 | {
48 | HttpGet get = new HttpGet(_jwksUri);
49 |
50 | get.setHeader(ACCEPT, ContentType.APPLICATION_JSON.getMimeType());
51 |
52 | HttpResponse response = _httpClient.execute(get);
53 |
54 | if (response.getStatusLine().getStatusCode() != HttpStatus.SC_OK)
55 | {
56 | _logger.severe(() -> "Got error from Jwks server: " + response.getStatusLine().getStatusCode());
57 |
58 | throw new IOException("Got error from Jwks server: " + response.getStatusLine().getStatusCode());
59 | }
60 |
61 | return EntityUtils.toString(response.getEntity(), StandardCharsets.UTF_8);
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/EdDSAPublicKeyCreator.java:
--------------------------------------------------------------------------------
1 | package io.curity.oauth;
2 |
3 | import java.math.BigInteger;
4 | import java.security.KeyFactory;
5 | import java.security.NoSuchAlgorithmException;
6 | import java.security.PublicKey;
7 | import java.security.spec.EdECPoint;
8 | import java.security.spec.EdECPublicKeySpec;
9 | import java.security.spec.InvalidKeySpecException;
10 | import java.security.spec.NamedParameterSpec;
11 | import java.util.Base64;
12 |
13 | final class EdDSAPublicKeyCreator {
14 |
15 | static PublicKey createPublicKey(String curveName, String publicKeyJwt) throws InvalidKeySpecException, NoSuchAlgorithmException
16 | {
17 | Base64.Decoder decoder = Base64.getUrlDecoder();
18 | byte[] publicKeyBytes = decoder.decode(publicKeyJwt);
19 | int b = publicKeyBytes.length;
20 |
21 | if (b != 32 && b != 57) {
22 | throw new InvalidKeySpecException("Invalid key length for EdDSA key.");
23 | }
24 |
25 | // Byte array is in little endian encoding, the most significant bit in final octet indicates if X is negative or not:
26 | // https://www.rfc-editor.org/rfc/rfc8032.html#section-3.1
27 | // https://www.rfc-editor.org/rfc/rfc8032.html#section-5.1.2
28 | boolean XIsNegative = (publicKeyBytes[b-1] & 0x80) != 0;
29 | // Recover y value by clearing x-bit.
30 | publicKeyBytes[b-1] = (byte)(publicKeyBytes[b-1] & 0x7f);
31 |
32 | byte[] publicKeyBytesBE = new byte[b];
33 |
34 | // Switch to big endian encoding
35 | for(int i = 0; i < b; i++) {
36 | publicKeyBytesBE[i] = publicKeyBytes[b-1-i];
37 | }
38 |
39 | // Create key from specs
40 | NamedParameterSpec crvKeySpec = new NamedParameterSpec(curveName);
41 | EdECPublicKeySpec edECPublicKeySpec = new EdECPublicKeySpec(crvKeySpec, new EdECPoint(XIsNegative, new BigInteger(1, publicKeyBytesBE)));
42 | KeyFactory keyFactory = KeyFactory.getInstance("EdDSA");
43 |
44 | return keyFactory.generatePublic(edECPublicKeySpec);
45 | }
46 |
47 | }
48 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/Expirable.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import java.time.Instant;
20 |
21 | public interface Expirable
22 | {
23 | Instant getExpiresAt();
24 | }
25 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/ExpirationBasedCache.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import java.time.Clock;
20 | import java.time.Instant;
21 | import java.util.HashSet;
22 | import java.util.Optional;
23 | import java.util.Set;
24 | import java.util.Timer;
25 | import java.util.TimerTask;
26 | import java.util.concurrent.ConcurrentHashMap;
27 |
28 | /**
29 | * A cache that expires it's entries after a given timeout
30 | * @param The key
31 | * @param A value that can expire
32 | */
33 | public class ExpirationBasedCache
34 | {
35 | private final ConcurrentHashMap _cache;
36 | private final Clock _clock;
37 |
38 | ExpirationBasedCache()
39 | {
40 | _cache = new ConcurrentHashMap<>();
41 | _clock = Clock.systemUTC();
42 |
43 | Timer timer = new Timer("cacheExpiration", true);
44 | timer.scheduleAtFixedRate(new TimerTask() {
45 | @Override
46 | public void run() {
47 | expireCacheEntries();
48 | }} , 60, 60);
49 | }
50 |
51 | public Optional get(K key)
52 | {
53 | //optimistically get a value
54 | V value = _cache.get(key);
55 |
56 | //make sure it's not expired yet
57 | if (value != null && value.getExpiresAt().isAfter(Instant.now(_clock)))
58 | {
59 | value = null;
60 | }
61 |
62 | return Optional.ofNullable(value);
63 | }
64 |
65 | void put(K key, V value)
66 | {
67 | _cache.putIfAbsent(key, value);
68 | }
69 |
70 | private void expireCacheEntries()
71 | {
72 | Instant now = Instant.now(_clock);
73 | //This might miss the last entry if new are put in, but that's ok
74 | //it will be caught in the next expiration round instead.
75 | Set keySet = new HashSet<>(_cache.keySet());
76 |
77 | for (K key : keySet)
78 | {
79 | V entry = _cache.get(key);
80 |
81 | if (now.isAfter(entry.getExpiresAt()))
82 | {
83 | _cache.remove(key);
84 | }
85 | }
86 | }
87 |
88 | void clear()
89 | {
90 | _cache.clear();
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/FilterHelper.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import jakarta.servlet.FilterConfig;
20 | import jakarta.servlet.UnavailableException;
21 | import java.util.Enumeration;
22 | import java.util.LinkedHashMap;
23 | import java.util.Map;
24 | import java.util.Optional;
25 | import java.util.function.Function;
26 |
27 | final class FilterHelper
28 | {
29 | private FilterHelper()
30 | {
31 | // no instantiation - static functions only
32 | }
33 |
34 | static Map initParamsMapFrom(FilterConfig config)
35 | {
36 | Map result = new LinkedHashMap<>();
37 | Enumeration names = config.getInitParameterNames();
38 |
39 | while (names.hasMoreElements())
40 | {
41 | String name = names.nextElement();
42 |
43 | result.put(name, config.getInitParameter(name));
44 | }
45 |
46 | return result;
47 | }
48 |
49 | static String getInitParamValue(String name, Map initParams) throws UnavailableException
50 | {
51 | Optional value = getSingleValue(name, initParams);
52 |
53 | if (value.isPresent())
54 | {
55 | return value.get();
56 | }
57 | else
58 | {
59 | throw new UnavailableException(missingInitParamMessage(name));
60 | }
61 | }
62 |
63 | static T getInitParamValue(String name, Map initParams,
64 | Function converter) throws UnavailableException
65 | {
66 | return converter.apply(getInitParamValue(name, initParams));
67 | }
68 |
69 | static Optional getOptionalInitParamValue(String name, Map initParams,
70 | Function converter) throws UnavailableException
71 | {
72 | Optional value = getSingleValue(name, initParams);
73 |
74 | return value.flatMap(s -> Optional.ofNullable(converter.apply(s)));
75 | }
76 |
77 | private static Optional getSingleValue(String name, Map initParams) throws
78 | UnavailableException
79 | {
80 | return Optional.ofNullable(initParams.get(name)).map(Object::toString);
81 | }
82 |
83 | private static String missingInitParamMessage(String paramName)
84 | {
85 | return String.format("%s - missing required initParam [%s]",
86 | OAuthFilter.class.getName(),
87 | paramName);
88 | }
89 | }
90 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/HttpClientProvider.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import jakarta.servlet.UnavailableException;
20 | import java.util.Iterator;
21 | import java.util.Map;
22 | import java.util.ServiceLoader;
23 |
24 | public abstract class HttpClientProvider
25 | {
26 | public HttpClientProvider()
27 | {
28 | }
29 |
30 | static HttpClientProvider provider()
31 | {
32 | ServiceLoader loader = ServiceLoader.load(HttpClientProvider.class);
33 | Iterator it = loader.iterator();
34 |
35 | if (it.hasNext())
36 | {
37 | return it.next();
38 | }
39 |
40 | return new DefaultHttpClientProvider();
41 | }
42 |
43 | public abstract IntrospectionClient createIntrospectionClient(Map config) throws UnavailableException;
44 |
45 | public abstract WebKeysClient createWebKeysClient(Map config) throws UnavailableException;
46 | }
47 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/IntrospectionClient.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import java.io.Closeable;
20 | import java.io.IOException;
21 |
22 | public interface IntrospectionClient extends Closeable
23 | {
24 | String introspect(String token) throws IOException;
25 | }
26 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/JsonData.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import javax.json.JsonObject;
20 | import javax.json.JsonValue;
21 | import java.time.Instant;
22 | import java.util.Set;
23 |
24 | public class JsonData implements Expirable
25 | {
26 | private final JsonObject _jsonObject;
27 | private final Set _scopes;
28 |
29 | JsonData(JsonObject jsonObject)
30 | {
31 | _jsonObject = jsonObject;
32 | _scopes = JsonUtils.getScopes(jsonObject);
33 | }
34 |
35 | JsonObject getJsonObject()
36 | {
37 | return _jsonObject;
38 | }
39 |
40 | public String getSubject()
41 | {
42 | return JsonUtils.getString(_jsonObject, "sub");
43 | }
44 |
45 | public Set getScopes()
46 | {
47 | return _scopes;
48 | }
49 |
50 | public Set getClaimNames()
51 | {
52 | return _jsonObject.keySet();
53 | }
54 |
55 | public JsonObject getClaims()
56 | {
57 | return _jsonObject;
58 | }
59 |
60 | public JsonValue getClaim(String claimName)
61 | {
62 | return _jsonObject.get(claimName);
63 | }
64 |
65 | @Override
66 | public Instant getExpiresAt()
67 | {
68 | return Instant.ofEpochSecond(JsonUtils.getLong(_jsonObject, "exp"));
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/JsonUtils.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2017 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import javax.json.JsonNumber;
20 | import javax.json.JsonObject;
21 | import javax.json.JsonReaderFactory;
22 | import javax.json.JsonString;
23 | import javax.json.JsonValue;
24 | import javax.json.spi.JsonProvider;
25 | import java.util.Arrays;
26 | import java.util.Collections;
27 | import java.util.HashSet;
28 | import java.util.Optional;
29 | import java.util.Set;
30 |
31 | final class JsonUtils
32 | {
33 | private static final String[] NO_SCOPES = {};
34 |
35 | private JsonUtils()
36 | {
37 | }
38 |
39 | static JsonReaderFactory createDefaultReaderFactory()
40 | {
41 | return JsonProvider.provider().createReaderFactory(Collections.emptyMap());
42 | }
43 |
44 | static Set getScopes(JsonObject jsonObject)
45 | {
46 | String scopesInToken = getString(jsonObject, "scope");
47 | String[] presentedScopes = scopesInToken == null ? NO_SCOPES : scopesInToken.split("\\s+");
48 |
49 | return Collections.unmodifiableSet(new HashSet<>(Arrays.asList(presentedScopes)));
50 | }
51 |
52 | static String getString(JsonObject jsonObject, String name)
53 | {
54 | return Optional.ofNullable(jsonObject.get(name))
55 | .filter(it -> it.getValueType() == JsonValue.ValueType.STRING)
56 | .map(it -> ((JsonString) it).getString())
57 | .orElse(null);
58 | }
59 |
60 | static long getLong(JsonObject jsonObject, String name)
61 | {
62 | return Optional.ofNullable(jsonObject.get(name))
63 | .filter(it -> it.getValueType() == JsonValue.ValueType.NUMBER)
64 | .map(it -> ((JsonNumber) it).longValue())
65 | .orElse(Long.MIN_VALUE);
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/JsonWebKey.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import javax.json.JsonObject;
20 | import javax.json.JsonString;
21 | import javax.json.JsonValue;
22 | import java.util.Optional;
23 |
24 | class JsonWebKey
25 | {
26 | private final JsonObject _jsonObject;
27 |
28 | private JsonWebKeyType _keyType;
29 |
30 | JsonWebKey(JsonObject jsonObject)
31 | {
32 | _jsonObject = jsonObject;
33 |
34 | JsonValue jsonValue = jsonObject.get("kty");
35 |
36 | _keyType = JsonWebKeyType.from(jsonValue);
37 | }
38 |
39 | String getKeyId()
40 | {
41 | return getString("kid");
42 | }
43 |
44 | JsonWebKeyType getKeyType()
45 | {
46 | return _keyType;
47 | }
48 |
49 | String getUse()
50 | {
51 | return getString("use");
52 | }
53 |
54 | String getXCoordinate()
55 | {
56 | return getString("x");
57 | }
58 |
59 | String getYCoordinate()
60 | {
61 | return getString("y");
62 | }
63 |
64 | String getEllipticalCurve()
65 | {
66 | return getString("crv");
67 | }
68 |
69 | String getModulus()
70 | {
71 | return getString("n");
72 | }
73 |
74 | String getExponent()
75 | {
76 | return getString("e");
77 | }
78 |
79 | String getAlgorithm()
80 | {
81 | return getString("alg");
82 | }
83 |
84 | private String getString(String name)
85 | {
86 | return Optional.ofNullable(_jsonObject.get(name))
87 | .filter(it -> it.getValueType() == JsonValue.ValueType.STRING)
88 | .map(it -> ((JsonString) it).getString())
89 | .orElse(null);
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/JsonWebKeyNotFoundException.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import java.io.IOException;
20 |
21 | class JsonWebKeyNotFoundException extends IOException
22 | {
23 | JsonWebKeyNotFoundException(String msg)
24 | {
25 | super(msg);
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/JsonWebKeyType.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import javax.json.JsonString;
20 | import javax.json.JsonValue;
21 | import java.util.logging.Logger;
22 |
23 | enum JsonWebKeyType {
24 | EC("EC"),
25 | OCT("oct"),
26 | OKP("OKP"),
27 | RSA("RSA"),
28 | UNSPECIFIED("UNSPECIFIED");
29 |
30 | private static final Logger _logger = Logger.getLogger(JsonWebKeyType.class.getName());
31 | String name;
32 |
33 | JsonWebKeyType(String name) {
34 | this.name = name;
35 | }
36 |
37 | static JsonWebKeyType from(JsonValue value) {
38 | if (value == null || value.toString().length() == 0) {
39 | return UNSPECIFIED;
40 | }
41 |
42 | if (value.getValueType() != JsonValue.ValueType.STRING) {
43 | _logger.warning(() -> String.format("Value '%s' is not a string, as required; it is %s",
44 | value, value.getValueType()));
45 | }
46 |
47 | switch (((JsonString) value).getString()) {
48 | case "RSA":
49 | return RSA;
50 | case "EC":
51 | return EC;
52 | case "OKP":
53 | return OKP;
54 | case "oct":
55 | return OCT;
56 | default:
57 |
58 | _logger.warning(() -> String.format("Unknown enumeration value '%s' given.", value));
59 |
60 | throw new IllegalArgumentException("value");
61 | }
62 | }
63 | }
64 |
65 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/JwkManager.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import javax.json.JsonObject;
20 | import javax.json.JsonReader;
21 | import javax.json.JsonReaderFactory;
22 | import java.io.Closeable;
23 | import java.io.IOException;
24 | import java.io.StringReader;
25 | import java.time.Duration;
26 | import java.time.Instant;
27 | import java.util.Collections;
28 | import java.util.HashMap;
29 | import java.util.Map;
30 | import java.util.concurrent.Executors;
31 | import java.util.concurrent.ScheduledExecutorService;
32 | import java.util.concurrent.TimeUnit;
33 | import java.util.logging.Level;
34 | import java.util.logging.Logger;
35 |
36 | final class JwkManager implements Closeable
37 | {
38 | private static final Logger _logger = Logger.getLogger(JwkManager.class.getName());
39 | private static final String ACCEPT = "Accept";
40 |
41 | private final TimeBasedCache _jsonWebKeyByKID;
42 | private final WebKeysClient _webKeysClient;
43 | private final ScheduledExecutorService _executor = Executors.newSingleThreadScheduledExecutor();
44 | private final JsonReaderFactory _jsonReaderFactory;
45 |
46 | JwkManager(long minKidReloadTimeInSeconds, WebKeysClient webKeysClient, JsonReaderFactory jsonReaderFactory)
47 | {
48 | _jsonWebKeyByKID = new TimeBasedCache<>(Duration.ofSeconds(minKidReloadTimeInSeconds), this::reload);
49 | _webKeysClient = webKeysClient;
50 | _jsonReaderFactory = jsonReaderFactory;
51 |
52 | // invalidate the cache periodically to avoid stale state
53 | _executor.scheduleAtFixedRate(this::ensureCacheIsFresh, 5, 15, TimeUnit.MINUTES);
54 | }
55 |
56 | /**
57 | * checks if the JsonWebKey exists in the local cached, otherwise this
58 | * method will call the JsonWebKeyService to get the new keys.
59 | *
60 | * @param keyId keyId
61 | * @return JsonWebKey
62 | */
63 | JsonWebKey getJsonWebKeyForKeyId(String keyId) throws JsonWebKeyNotFoundException
64 | {
65 | JsonWebKey key = _jsonWebKeyByKID.get(keyId);
66 |
67 | if (key != null)
68 | {
69 | return key;
70 | }
71 |
72 | throw new JsonWebKeyNotFoundException("Json Web Key does not exist: keyid=" + keyId);
73 | }
74 |
75 | private Map reload()
76 | {
77 | Map newKeys = new HashMap<>();
78 |
79 | try
80 | {
81 | JwksResponse response = parseJwksResponse(_webKeysClient.getKeys());
82 |
83 | for (JsonWebKey key : response.getKeys())
84 | {
85 | newKeys.put(key.getKeyId(), key);
86 | }
87 |
88 | _logger.info(() -> String.format("Fetched JsonWebKeys: %s", newKeys));
89 |
90 | return Collections.unmodifiableMap(newKeys);
91 | }
92 | catch (IOException e)
93 | {
94 | _logger.log(Level.SEVERE, "Could not contact JWKS Server", e);
95 |
96 | return Collections.emptyMap();
97 | }
98 | }
99 |
100 | private JwksResponse parseJwksResponse(String response)
101 | {
102 | JsonReader jsonReader = _jsonReaderFactory.createReader(new StringReader(response));
103 | JsonObject jsonObject = jsonReader.readObject();
104 |
105 | return new JwksResponse(jsonObject);
106 | }
107 |
108 | private void ensureCacheIsFresh()
109 | {
110 | _logger.info("Called ensureCacheIsFresh");
111 |
112 | Instant lastLoading = _jsonWebKeyByKID.getLastReloadInstant().orElse(Instant.MIN);
113 | boolean cacheIsNotFresh = lastLoading.isBefore(Instant.now()
114 | .minus(_jsonWebKeyByKID.getMinTimeBetweenReloads()));
115 |
116 | if (cacheIsNotFresh)
117 | {
118 | _logger.info("Invalidating JSON WebKeyID cache");
119 |
120 | _jsonWebKeyByKID.clear();
121 | }
122 | }
123 |
124 | @Override
125 | public void close() throws IOException
126 | {
127 | _executor.shutdown();
128 |
129 | if (_webKeysClient instanceof Closeable)
130 | {
131 | ((Closeable) _webKeysClient).close();
132 | }
133 | }
134 | }
135 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/JwksResponse.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import javax.json.JsonObject;
20 | import javax.json.JsonValue;
21 | import java.util.Collections;
22 | import java.util.List;
23 | import java.util.stream.Collectors;
24 |
25 | class JwksResponse
26 | {
27 | private final List _keys;
28 |
29 | JwksResponse(JsonObject jsonObject)
30 | {
31 | JsonValue keys = jsonObject.get("keys");
32 |
33 | if (keys.getValueType() != JsonValue.ValueType.ARRAY)
34 | {
35 | _keys = Collections.emptyList();
36 | }
37 | else
38 | {
39 | _keys = keys.asJsonArray().stream()
40 | .filter(it -> it.getValueType() == JsonValue.ValueType.OBJECT)
41 | .map(JsonValue::asJsonObject)
42 | .map(JsonWebKey::new)
43 | .collect(Collectors.toList());
44 | }
45 | }
46 |
47 | List getKeys()
48 | {
49 | return _keys;
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/JwtValidator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | interface JwtValidator extends TokenValidator
20 | {
21 | /**
22 | * The Jwt validator is a class that takes a JWT and makes sure the Signature is valid
23 | * @param jwt the JWT to be validated
24 | * @return the content of the token body if token signature is valid, otherwise null
25 | * @throws TokenValidationException
26 | */
27 | JsonData validate(String jwt) throws TokenValidationException;
28 | }
29 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/JwtValidatorWithCert.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import javax.json.JsonReaderFactory;
20 | import java.security.PublicKey;
21 | import java.util.Map;
22 | import java.util.Optional;
23 | import java.util.logging.Logger;
24 |
25 | final class JwtValidatorWithCert extends AbstractJwtValidator
26 | {
27 | private static final Logger _logger = Logger.getLogger(JwtValidatorWithCert.class.getName());
28 |
29 | private final Map _keys;
30 |
31 | JwtValidatorWithCert(String issuer, String audience, Map publicKeys)
32 | {
33 | this(issuer, audience, publicKeys, JsonUtils.createDefaultReaderFactory());
34 | }
35 |
36 | JwtValidatorWithCert(String issuer, String audience, Map publicKeys,
37 | JsonReaderFactory jsonReaderFactory)
38 | {
39 | super(issuer, audience, jsonReaderFactory);
40 |
41 | _keys = publicKeys;
42 | }
43 |
44 | @Override
45 | protected Optional getPublicKey(JwtHeader jwtHeader)
46 | {
47 | return Optional.ofNullable(_keys.get(jwtHeader.getString("x5t#S256")));
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/JwtValidatorWithJwk.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import javax.json.JsonReaderFactory;
20 | import java.io.IOException;
21 | import java.security.NoSuchAlgorithmException;
22 | import java.security.PublicKey;
23 | import java.security.spec.InvalidKeySpecException;
24 | import java.util.Optional;
25 | import java.util.logging.Level;
26 | import java.util.logging.Logger;
27 |
28 | final class JwtValidatorWithJwk extends AbstractJwtValidator
29 | {
30 | private static final Logger _logger = Logger.getLogger(JwtValidatorWithJwk.class.getName());
31 |
32 | private final JwkManager _jwkManager;
33 |
34 | JwtValidatorWithJwk(long minKidReloadTime, WebKeysClient webKeysClient, String audience, String issuer,
35 | JsonReaderFactory jsonReaderFactory)
36 | {
37 | super(issuer, audience, jsonReaderFactory);
38 |
39 | _jwkManager = new JwkManager(minKidReloadTime, webKeysClient, jsonReaderFactory);
40 | }
41 |
42 | @Override
43 | protected Optional getPublicKey(JwtHeader jwtHeader)
44 | {
45 | Optional result = Optional.empty();
46 |
47 | try
48 | {
49 | JsonWebKey jsonWebKeyType = _jwkManager.getJsonWebKeyForKeyId(jwtHeader.getKeyId());
50 |
51 | switch (jsonWebKeyType.getKeyType()) {
52 | case RSA :
53 | result = Optional.of(RsaPublicKeyCreator.createPublicKey(jsonWebKeyType.getModulus(),
54 | jsonWebKeyType.getExponent()));
55 | break;
56 | case OKP :
57 | if (isEdDSAKey(jsonWebKeyType)) {
58 | result = Optional.of(EdDSAPublicKeyCreator.createPublicKey(jsonWebKeyType.getEllipticalCurve(), jsonWebKeyType.getXCoordinate()));
59 | } else {
60 | throw new NoSuchAlgorithmException(String.format("Unsupported curve %s for key %s", jsonWebKeyType.getEllipticalCurve(), jsonWebKeyType.getKeyId()));
61 | }
62 | break;
63 | case EC :
64 | case OCT :
65 | default:
66 | throw new NoSuchAlgorithmException(String.format("Unsupported key type %s for key %s", jsonWebKeyType.getKeyType(), jsonWebKeyType.getKeyId()));
67 | }
68 | }
69 | catch (JsonWebKeyNotFoundException e)
70 | {
71 | // this is not a very exceptional occurrence, so let's not log a stack-trace
72 | _logger.info(() -> String.format("Could not find requested JsonWebKey: %s", e));
73 | }
74 | catch (NoSuchAlgorithmException | InvalidKeySpecException e)
75 | {
76 | _logger.log(Level.WARNING, "Could not create public key", e);
77 | }
78 |
79 | return result;
80 | }
81 |
82 | private boolean isEdDSAKey(JsonWebKey jsonWebKey) {
83 | String curve = jsonWebKey.getEllipticalCurve();
84 | return curve != null && (curve.equals("Ed25519") || curve.equals("Ed448"));
85 | }
86 |
87 | @Override
88 | public void close() throws IOException
89 | {
90 | _jwkManager.close();
91 | }
92 | }
93 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/OAuthFilter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import jakarta.servlet.Filter;
20 | import jakarta.servlet.FilterChain;
21 | import jakarta.servlet.FilterConfig;
22 | import jakarta.servlet.ServletException;
23 | import jakarta.servlet.ServletRequest;
24 | import jakarta.servlet.ServletResponse;
25 | import jakarta.servlet.UnavailableException;
26 | import jakarta.servlet.http.HttpServletRequest;
27 | import jakarta.servlet.http.HttpServletResponse;
28 | import java.io.IOException;
29 | import java.util.Arrays;
30 | import java.util.List;
31 | import java.util.Map;
32 | import java.util.Optional;
33 | import java.util.logging.Level;
34 | import java.util.logging.Logger;
35 |
36 | public abstract class OAuthFilter implements Filter
37 | {
38 | private static final String[] NO_SCOPES = {};
39 | private static final Logger _logger = Logger.getLogger(OAuthFilter.class.getName());
40 | private static final String WWW_AUTHENTICATE = "WWW-Authenticate";
41 | private static final String AUTHORIZATION = "Authorization";
42 | public static final String PRINCIPAL_ATTRIBUTE_NAME = "principal";
43 |
44 | private Map _filterConfig; // Protected, so subclasses don't have to repeat the conversion to this
45 | private String _oauthHost = null;
46 | private String[] _scopes = null;
47 |
48 | private interface InitParams
49 | {
50 | String OAUTH_HOST = "oauthHost";
51 | String SCOPE = "scope";
52 | }
53 |
54 | @Override
55 | public void init(FilterConfig filterConfig) throws ServletException
56 | {
57 | _filterConfig = FilterHelper.initParamsMapFrom(filterConfig);
58 |
59 | _oauthHost = FilterHelper.getInitParamValue(InitParams.OAUTH_HOST, _filterConfig);
60 |
61 | _scopes = FilterHelper.getOptionalInitParamValue(InitParams.SCOPE, _filterConfig, it -> it.split("\\s+"))
62 | .orElse(NO_SCOPES);
63 | }
64 |
65 | /**
66 | * The doFilter is the primary filter method of a Servlet filter. It is implemented as a final method
67 | * and will call the configured filters authenticate and authorize methods.
68 | * Authorize is optional to implement as this filter implements a default scope check method.
69 | * @param servletRequest The default servlet request
70 | * @param servletResponse The default servlet response
71 | * @param filterChain A filter chain to continue with after this filter is done
72 | * @throws IOException when response fails to send an error
73 | * @throws ServletException when authentication fails for some exceptional reason
74 | */
75 | @Override
76 | public final void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain)
77 | throws IOException, ServletException
78 | {
79 | HttpServletResponse response = (HttpServletResponse)servletResponse;
80 | Optional token = extractAccessTokenFromHeader(servletRequest);
81 | String oauthHost = getOAuthServerRealm();
82 |
83 | if (!token.isPresent())
84 | {
85 | setReAuthenticate401(response, oauthHost);
86 |
87 | return;
88 | }
89 |
90 | Optional maybeAuthenticatedUser = authenticate(token.get());
91 |
92 | if (!maybeAuthenticatedUser.isPresent())
93 | {
94 | setReAuthenticate401(response, oauthHost);
95 |
96 | return;
97 | }
98 |
99 | AuthenticatedUser authenticatedUser = maybeAuthenticatedUser.get();
100 |
101 | if (!isAuthorized(authenticatedUser))
102 | {
103 | //403 Forbidden Scope header
104 | setForbidden403(response, oauthHost);
105 |
106 | return;
107 | }
108 |
109 | servletRequest.setAttribute(PRINCIPAL_ATTRIBUTE_NAME, authenticatedUser);
110 |
111 | if (filterChain != null)
112 | {
113 | filterChain.doFilter(
114 | new AuthenticatedUserRequestWrapper((HttpServletRequest)servletRequest, authenticatedUser),
115 | servletResponse);
116 | }
117 | }
118 |
119 | protected Map getFilterConfiguration()
120 | {
121 | return _filterConfig;
122 | }
123 |
124 | private void setReAuthenticate401(HttpServletResponse response, String oauthHost) throws IOException
125 | {
126 | String msg = String.format("Bearer realm=\"%s\"", oauthHost);
127 |
128 | response.setHeader(WWW_AUTHENTICATE, msg);
129 | response.sendError(HttpServletResponse.SC_UNAUTHORIZED);
130 | }
131 |
132 | private void setForbidden403(HttpServletResponse response, String oauthHost) throws IOException
133 | {
134 | String msg = String.format("Bearer realm=\"%s\"", oauthHost);
135 |
136 | response.setHeader(WWW_AUTHENTICATE, msg);
137 | response.sendError(HttpServletResponse.SC_FORBIDDEN);
138 | }
139 |
140 | /**
141 | * Returns the realm of the OAuth server.
142 | *
143 | *
This is used when the filter returns 401, Access Denied, or 403, Forbiggen, with a WWW-Authenticate HTTP
144 | * indicating to the client that authentication is required
145 | *
146 | * @return The OAuth server's realm as string
147 | * @throws UnavailableException when filter is not initialized
148 | */
149 | protected String getOAuthServerRealm() throws UnavailableException
150 | {
151 | if (_oauthHost == null)
152 | {
153 | throw new UnavailableException("Filter not initialized");
154 | }
155 |
156 | return _oauthHost;
157 | }
158 |
159 | protected abstract TokenValidator createTokenValidator(Map initParams) throws UnavailableException;
160 |
161 | protected abstract TokenValidator getTokenValidator();
162 |
163 | /**
164 | * This is the authenticate method of the filter, it will take the token as string input and
165 | * must perform the appropriate operation to validate the token.
166 | * @param token - The token extracted from the Authorization header and stripped of the Bearer
167 | * @return An AuthenticatedUser if the token was valid, or null if not.
168 | * @throws ServletException when authentication fails for some exceptional reason
169 | */
170 | protected Optional authenticate(String token) throws ServletException
171 | {
172 | AuthenticatedUser result = null;
173 |
174 | try
175 | {
176 | JsonData validationResult = getTokenValidator().validate(token);
177 |
178 | result = AuthenticatedUser.from(validationResult);
179 | }
180 | catch (Exception e)
181 | {
182 | _logger.fine(() -> String.format("Failed to validate incoming token due to: %s", e.getMessage()));
183 | }
184 |
185 | return Optional.ofNullable(result);
186 | }
187 | /**
188 | * Authorizes the current request by checking that all configured scopes are included in the one presented in the
189 | * request.
190 | *
191 | *
If no scopes were configured for the filter, then any request is authorized. When a set of scopes are
192 | * configured, however, the filter will ensure that all such scopes are included in the presented token.
193 | *
194 | * @param authenticatedUser the user that was authenticated
195 | * @return true if access is allowed
196 | */
197 | protected boolean isAuthorized(AuthenticatedUser authenticatedUser)
198 | {
199 | List requiredScopes = Arrays.asList(_scopes);
200 |
201 | // No scopes required for authorization
202 | return requiredScopes.isEmpty() || authenticatedUser.getScopes().containsAll(requiredScopes);
203 | }
204 |
205 | @Override
206 | public void destroy()
207 | {
208 | _logger.info("Destroying OAuthFilter");
209 |
210 | if (getTokenValidator() != null)
211 | {
212 | try
213 | {
214 | getTokenValidator().close();
215 | }
216 | catch (IOException e)
217 | {
218 | _logger.log(Level.WARNING, "Problem closing token validator", e);
219 | }
220 | }
221 | }
222 |
223 | /**
224 | * Extracts the token from the Authorization header, removing the Bearer prefix
225 | * @param request The incoming request
226 | * @return the token or null if not present
227 | */
228 | private Optional extractAccessTokenFromHeader(ServletRequest request)
229 | {
230 | HttpServletRequest httpRequest = (HttpServletRequest)request;
231 | String authorizationHeader = httpRequest.getHeader(AUTHORIZATION);
232 | String result = null;
233 |
234 | if (authorizationHeader != null && authorizationHeader.startsWith("Bearer"))
235 | {
236 | String[] tokenSplit = authorizationHeader.split("[Bb][Ee][Aa][Rr][Ee][Rr]\\s+");
237 |
238 | if(tokenSplit.length != 2)
239 | {
240 | _logger.fine("Incoming token in Authorization header is not a Bearer token");
241 | }
242 | else
243 | {
244 | result = tokenSplit[1];
245 | }
246 | }
247 |
248 | return Optional.ofNullable(result);
249 | }
250 | }
251 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/OAuthIntrospectResponse.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import javax.json.JsonObject;
20 |
21 | class OAuthIntrospectResponse
22 | {
23 | private final JsonObject _jsonObject;
24 |
25 | OAuthIntrospectResponse(JsonObject jsonObject)
26 | {
27 | _jsonObject = jsonObject;
28 | }
29 |
30 | boolean isActive()
31 | {
32 | return _jsonObject.getBoolean("active");
33 | }
34 |
35 | JsonObject getJsonObject()
36 | {
37 | return _jsonObject;
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/OAuthJwtFilter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import javax.json.JsonReaderFactory;
20 | import javax.json.spi.JsonProvider;
21 | import jakarta.servlet.FilterConfig;
22 | import jakarta.servlet.ServletException;
23 | import jakarta.servlet.UnavailableException;
24 | import java.util.Map;
25 | import java.util.logging.Logger;
26 |
27 | public class OAuthJwtFilter extends OAuthFilter
28 | {
29 | private static final Logger _logger = Logger.getLogger(OAuthJwtFilter.class.getName());
30 |
31 | private long _minKidReloadTimeInSeconds = 3600;
32 |
33 | private TokenValidator _jwtValidator = null;
34 |
35 | private interface InitParams
36 | {
37 | String ISSUER = "issuer";
38 | String AUDIENCE = "audience";
39 | String MIN_KID_RELOAD_TIME = "_minKidReloadTimeInSeconds";
40 | }
41 |
42 | @Override
43 | public void init(FilterConfig filterConfig) throws ServletException
44 | {
45 | super.init(filterConfig);
46 |
47 | _minKidReloadTimeInSeconds = FilterHelper.getOptionalInitParamValue(
48 | InitParams.MIN_KID_RELOAD_TIME,
49 | getFilterConfiguration(), Long::parseLong).orElse(_minKidReloadTimeInSeconds);
50 |
51 | synchronized (this)
52 | {
53 | if (_jwtValidator == null)
54 | {
55 | _jwtValidator = createTokenValidator(getFilterConfiguration());
56 |
57 | _logger.info(() -> String.format("%s successfully initialized", OAuthFilter.class.getSimpleName()));
58 | }
59 | else
60 | {
61 | _logger.warning("Attempted to set webkey URI more than once! Ignoring further attempts.");
62 | }
63 | }
64 | }
65 |
66 | @Override
67 | protected TokenValidator createTokenValidator(Map filterConfig) throws UnavailableException
68 | {
69 | // Pass all of the filter's config to the ReaderFactory factory method. It'll ignore anything it doesn't
70 | // understand (per JSR 353). This way, clients can change the provider using the service locator and configure
71 | // the ReaderFactory using the filter's config.
72 | JsonReaderFactory jsonReaderFactory = JsonProvider.provider().createReaderFactory(filterConfig);
73 | WebKeysClient webKeysClient = HttpClientProvider.provider().createWebKeysClient(filterConfig);
74 | String audience = FilterHelper.getInitParamValue(InitParams.AUDIENCE, filterConfig);
75 | String issuer = FilterHelper.getInitParamValue(InitParams.ISSUER, filterConfig);
76 |
77 | return _jwtValidator = new JwtValidatorWithJwk(_minKidReloadTimeInSeconds, webKeysClient, audience, issuer,
78 | jsonReaderFactory);
79 | }
80 |
81 | @Override
82 | protected TokenValidator getTokenValidator()
83 | {
84 | return _jwtValidator;
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/OAuthOpaqueFilter.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import javax.json.JsonReaderFactory;
20 | import javax.json.spi.JsonProvider;
21 | import jakarta.servlet.FilterConfig;
22 | import jakarta.servlet.ServletException;
23 | import jakarta.servlet.UnavailableException;
24 | import java.util.Map;
25 | import java.util.logging.Logger;
26 |
27 | public class OAuthOpaqueFilter extends OAuthFilter
28 | {
29 | private static final Logger _logger = Logger.getLogger(OAuthOpaqueFilter.class.getName());
30 |
31 | private TokenValidator _opaqueTokenValidator = null;
32 |
33 | private interface InitParams
34 | {
35 | String SCOPE = "scope";
36 | }
37 |
38 | @Override
39 | public void init(FilterConfig filterConfig) throws ServletException
40 | {
41 | super.init(filterConfig);
42 |
43 | synchronized (this)
44 | {
45 | if (_opaqueTokenValidator == null)
46 | {
47 | _opaqueTokenValidator = createTokenValidator(getFilterConfiguration());
48 |
49 | _logger.info(() -> String.format("%s successfully initialized", OAuthFilter.class.getSimpleName()));
50 | }
51 | else
52 | {
53 | _logger.warning("Attempted to set introspect URI more than once! Ignoring further attempts.");
54 | }
55 | }
56 | }
57 |
58 | @Override
59 | protected TokenValidator getTokenValidator()
60 | {
61 | return _opaqueTokenValidator;
62 | }
63 |
64 | @Override
65 | protected TokenValidator createTokenValidator(Map initParams) throws UnavailableException
66 | {
67 | // Like in the OAuthJwtFilter, we'll reuse the config of this filter + the service locator to
68 | // get a JsonReaderFactory
69 | JsonReaderFactory jsonReaderFactory = JsonProvider.provider().createReaderFactory(initParams);
70 | IntrospectionClient introspectionClient = HttpClientProvider.provider()
71 | .createIntrospectionClient(initParams);
72 |
73 | return new OpaqueTokenValidator(introspectionClient, jsonReaderFactory);
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/OpaqueTokenValidator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import javax.json.JsonObject;
20 | import javax.json.JsonReader;
21 | import javax.json.JsonReaderFactory;
22 | import java.io.Closeable;
23 | import java.io.IOException;
24 | import java.io.StringReader;
25 | import java.time.Instant;
26 | import java.util.Optional;
27 |
28 | public class OpaqueTokenValidator implements Closeable, TokenValidator
29 | {
30 | private final IntrospectionClient _introspectionClient;
31 | private final ExpirationBasedCache _tokenCache;
32 | private final JsonReaderFactory _jsonReaderFactory;
33 |
34 | OpaqueTokenValidator(IntrospectionClient introspectionClient, JsonReaderFactory jsonReaderFactory)
35 | {
36 | _introspectionClient = introspectionClient;
37 | _tokenCache = new ExpirationBasedCache<>();
38 | _jsonReaderFactory = jsonReaderFactory;
39 | }
40 |
41 | public JsonData validate(String token) throws TokenValidationException
42 | {
43 | Optional cachedValue = _tokenCache.get(token);
44 |
45 | if (cachedValue.isPresent())
46 | {
47 | return cachedValue.get();
48 | }
49 |
50 | String introspectJson;
51 |
52 | try
53 | {
54 | introspectJson = _introspectionClient.introspect(token);
55 | }
56 | catch (Exception e)
57 | {
58 | // TODO: Add logging
59 | throw new TokenValidationException("Failed to introspect token", e);
60 | }
61 |
62 | OAuthIntrospectResponse response = parseIntrospectResponse(introspectJson);
63 |
64 | if (response.isActive())
65 | {
66 | JsonData newToken = new JsonData(response.getJsonObject());
67 |
68 | if (newToken.getExpiresAt().isAfter(Instant.now()))
69 | {
70 | //Note: If this cache is backed by some persistent storage, the token should be hashed and not stored
71 | // in clear text
72 | _tokenCache.put(token, newToken);
73 |
74 | return newToken;
75 | }
76 | else
77 | {
78 | throw new ExpiredTokenException();
79 | }
80 | }
81 | else
82 | {
83 | throw new RevokedTokenException();
84 | }
85 | }
86 |
87 | private OAuthIntrospectResponse parseIntrospectResponse(String introspectJson)
88 | {
89 | JsonReader jsonReader = _jsonReaderFactory.createReader(new StringReader(introspectJson));
90 | JsonObject jsonObject = jsonReader.readObject();
91 |
92 | return new OAuthIntrospectResponse(jsonObject);
93 | }
94 |
95 | @Override
96 | public void close() throws IOException
97 | {
98 | _introspectionClient.close();
99 | _tokenCache.clear();
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/RsaPublicKeyCreator.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import java.math.BigInteger;
20 | import java.security.KeyFactory;
21 | import java.security.NoSuchAlgorithmException;
22 | import java.security.PublicKey;
23 | import java.security.spec.InvalidKeySpecException;
24 | import java.security.spec.RSAPublicKeySpec;
25 | import java.util.Base64;
26 |
27 | class RsaPublicKeyCreator
28 | {
29 | static PublicKey createPublicKey(String modulus, String exponent) throws InvalidKeySpecException, NoSuchAlgorithmException
30 | {
31 | Base64.Decoder decoder = Base64.getUrlDecoder();
32 | BigInteger bigModulus = new BigInteger(1, decoder.decode(modulus));
33 | BigInteger bigExponent = new BigInteger(1, decoder.decode(exponent));
34 | RSAPublicKeySpec publicKeySpec = new RSAPublicKeySpec(bigModulus, bigExponent);
35 | KeyFactory keyFactory = KeyFactory.getInstance("RSA");
36 |
37 | return keyFactory.generatePublic(publicKeySpec);
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/src/main/java/io/curity/oauth/TimeBasedCache.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright (C) 2016 Curity AB.
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package io.curity.oauth;
18 |
19 | import java.time.Clock;
20 | import java.time.Duration;
21 | import java.time.Instant;
22 | import java.util.Collections;
23 | import java.util.Map;
24 | import java.util.Optional;
25 | import java.util.function.Supplier;
26 |
27 | /**
28 | * A Cache which attempts to reload its entries, limited to a minimum reloading time,
29 | * when a value is requested for a key which does not exist in the backing map.
30 | *
31 | * The cache will not attempt to reload more often than once every minTimeBetweenReloads,
32 | * which is a parameter provided in the constructor.
33 | *
34 | * This cache is Thread-safe.
35 | *
36 | * @param key type
37 | * @param value type
38 | */
39 | class TimeBasedCache
40 | {
41 | private final Object _cacheLock = new Object();
42 |
43 | private volatile Map _cache;
44 | private volatile Instant _nextLoadingEarliestTime = Instant.MIN;
45 |
46 | private volatile Instant _lastLoading;
47 |
48 | private final Duration _minTimeBetweenReloads;
49 | private final Supplier