@@ -34,8 +34,8 @@ Approov JWT token.
34
34
35
35
A custom payload claim in an Approov token is a base64 encoded sha256 hash of
36
36
some unique identifier we may want to tie with the Approov token to enhance
37
- the security on that request, like an OAUTH2 token, but you are free to use what
38
- so ever you may want.
37
+ the security on that request, like an Authorization token, but you are free to
38
+ use what so ever you may want.
39
39
40
40
Dummy example for the JWT token middle part, the payload:
41
41
@@ -56,24 +56,24 @@ The custom payload claim in an Approov token is the one in the `pay` key:
56
56
>
57
57
> Please bear in mind that the custom payload claim is not meant to pass
58
58
> application data to the API server, or any data that changes in every request,
59
- > but if this is an hard requirement for your use case, please contact us for
59
+ > but if this is a hard requirement for your use case, please contact us for
60
60
> further discussion.
61
61
62
62
63
63
## SYSTEM CLOCK
64
64
65
65
In order to correctly check for the expiration times of the Approov tokens is
66
- very important that the Java server is synchronizing automatically the system
67
- clock over the network with an authoritative time source. In Linux this is usual
68
- done with a NTP server.
66
+ important that the system clock for the Java server is synchronized
67
+ automatically over the network with an authoritative time source. In Linux this
68
+ is usual done with an NTP server.
69
69
70
70
71
71
## REQUIREMENTS
72
72
73
73
We will use Java ` 11.0.3 ` with the Spring Boot ` 2.1.3.RELEASE ` , and Gradle
74
74
` 5.2.1 ` to compile, build and run this demo.
75
75
76
- Docker is required for the ones wanting to use the Java docker stack provided
76
+ Docker is only required for developers wanting to use the Java docker stack provided
77
77
by the [ stack] ( ./stack ) bash script, that is a wrapper around docker commands.
78
78
79
79
``` bash
@@ -110,15 +110,15 @@ the API, but feel free to use any other tool of your preference.
110
110
We recommend the use of the included Docker stack to play with this Approov
111
111
integration.
112
112
113
- For details in how to use it you need to follow the setup instructions in the
113
+ For details on how to use it you need to follow the setup instructions in the
114
114
[ Approov Shapes Demo Server] ( ./docs/approov-shapes-demo-server.md#development-environment )
115
115
walk-through, but feel free to use your local environment to play with this
116
116
Approov integration.
117
117
118
118
119
119
## THE POSTMAN COLLECTION
120
120
121
- Import this [ Postman collection] ( https://gitlab.com/snippets/1799104 /raw ) that
121
+ Import this [ Postman collection] ( https://gitlab.com/snippets/1852520 /raw ) that
122
122
contains all the API endpoints for the Approov Shapes Demo Server and we
123
123
strongly recommend you to follow
124
124
[ this demo walk-through] ( ./docs/approov-shapes-demo-server.md ) after finishing
@@ -127,8 +127,8 @@ the Approov integration that we are about to start.
127
127
The Approov tokens used in the headers of this Postman collection where
128
128
generated by [ this helper script] ( ./bin/generate-token ) and
129
129
they cover all necessary scenarios, but feel free to use the script to generate
130
- some more valid and invalid tokens, with different expire times and custom
131
- payload claims. Some examples of using it can be found [ here] ( ./docs/approov-shapes-demo-server.md#approov-tokens -generation ) .
130
+ some more valid and invalid tokens, with different expiration times and custom
131
+ payload claims. Some examples of using it can be found [ here] ( ./docs/approov-shapes-demo-server.md#Approov-Tokens -generation ) .
132
132
133
133
134
134
## DEPENDENCIES
@@ -153,6 +153,7 @@ We will learn how to integrate Approov in a skeleton generated with Spring Boot,
153
153
where we added 3 endpoints:
154
154
155
155
* ` / ` - Not protected with Approov.
156
+ * ` /hello ` - Not protected with Approov.
156
157
* ` /shapes ` - Approov protected.
157
158
* ` /forms ` - Approov protected, and with a check for the Approov custom payload claim.
158
159
@@ -174,7 +175,7 @@ When implementing Approov is required to always check if the signature and
174
175
expiration time of the Approov token is valid, and optionally to check if the
175
176
custom payload claim matches the one in the header.
176
177
177
- For the required and optional checks we always need to configure the Spring
178
+ For both the required and optional checks we always need to configure the Spring
178
179
framework security with the ` ApproovAuthenticationProvider(approovConfig) ` .
179
180
180
181
Now we need to configure what endpoints will perform the required and optional
@@ -193,9 +194,9 @@ tell if the custom payload claim is to be checked or not, and this is done with
193
194
the boolean flag ` checkCustomPayload ` .
194
195
195
196
In order to be able to have endpoints that perform only the required checks in
196
- the Approov token, while at same time having others endpoints where both the
197
+ the Approov token, while at the same time having others endpoints where both the
197
198
required and optional checks must take place, we need to configure the Spring
198
- framework security context with static sub classes of the main ` WebSecurityConfig `
199
+ framework security context with static subclasses of the main ` WebSecurityConfig `
199
200
class, and this sub classes also need to implement the abstract
200
201
` WebSecurityConfigurerAdapter ` class. This subclasses will be annotated with a
201
202
configuration order ` @Order(n) ` , thus their configuration order is important. So
@@ -265,7 +266,6 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
265
266
CorsConfiguration configuration = new CorsConfiguration ();
266
267
configuration. setAllowedOrigins(Arrays . asList(" http://localhost:5000" ));
267
268
configuration. setAllowedMethods(Arrays . asList(" GET" ));
268
- configuration. addAllowedHeader(" Approov-Token" );
269
269
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource ();
270
270
source. registerCorsConfiguration(" /**" , configuration);
271
271
return source;
@@ -311,8 +311,8 @@ custom payload claim in the Approov token.
311
311
As already mentioned we will need to add to the ` WebSecurityConfig ` a subclass
312
312
for the endpoints we want to secure with only the required checks for an Approov
313
313
token, another for the endpoints secured with the required and optional checks
314
- for an Aprroov token, and finally a subclass for the endpoints that do not
315
- require authentication.
314
+ for an Aprroov token, and finally a subclass for endpoints that do not require
315
+ authentication.
316
316
317
317
So let's prepare the ` WebSecurityConfig ` with only a subclass that maintains the
318
318
access to all endpoints without any authentication.
@@ -348,7 +348,6 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
348
348
CorsConfiguration configuration = new CorsConfiguration ();
349
349
configuration. setAllowedOrigins(Arrays . asList(" http://localhost:5000" ));
350
350
configuration. setAllowedMethods(Arrays . asList(" GET" ));
351
- configuration. addAllowedHeader(" approov-token" );
352
351
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource ();
353
352
source. registerCorsConfiguration(" /**" , configuration);
354
353
return source;
@@ -390,6 +389,39 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
390
389
}
391
390
```
392
391
392
+ #### CORS Configuration
393
+
394
+ In order to integrate Approov we will need to use an ` Approov-Token ` , thus we
395
+ need to allow it in the CORS configuration.
396
+
397
+ If our Approov integration also uses the Approov custom payload check, then we
398
+ also need to allow the header from where we want to retrieve the value we bind
399
+ to the Approov token custom payload in the mobile app, that in this demo is the
400
+ ` Authorization ` header.
401
+
402
+ So we add to the CORS configuration this 2 new lines:
403
+
404
+ ``` java
405
+ configuration. addAllowedHeader(" Authorization" );
406
+ configuration. addAllowedHeader(" Approov-Token" );
407
+ ```
408
+
409
+ That will give us this new CORS configuration:
410
+
411
+ ``` java
412
+ @Bean
413
+ CorsConfigurationSource corsConfigurationSource() {
414
+ CorsConfiguration configuration = new CorsConfiguration ();
415
+ configuration. setAllowedOrigins(Arrays . asList(" http://localhost:5000" ));
416
+ configuration. setAllowedMethods(Arrays . asList(" GET" ));
417
+ configuration. addAllowedHeader(" Authorization" );
418
+ configuration. addAllowedHeader(" Approov-Token" );
419
+ UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource ();
420
+ source. registerCorsConfiguration(" /**" , configuration);
421
+ return source;
422
+ }
423
+ ```
424
+
393
425
#### Protecting the ` /shapes ` endpoint
394
426
395
427
To protect the ` /shapes ` endpoint we will add the subclass ` ApproovWebSecurityConfig ` :
@@ -555,7 +587,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
555
587
CorsConfiguration configuration = new CorsConfiguration ();
556
588
configuration. setAllowedOrigins(Arrays . asList(" http://localhost:5000" ));
557
589
configuration. setAllowedMethods(Arrays . asList(" GET" ));
558
- configuration. addAllowedHeader(" approov-token" );
590
+ configuration. addAllowedHeader(" Authorization" );
591
+ configuration. addAllowedHeader(" Approov-Token" );
559
592
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource ();
560
593
source. registerCorsConfiguration(" /**" , configuration);
561
594
return source;
@@ -617,12 +650,12 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
617
650
.securityContext()
618
651
.securityContextRepository(new ApproovSecurityContextRepository (approovConfig, true ))
619
652
.and()
620
- .exceptionHandling()
621
- .authenticationEntryPoint(new ApproovAuthenticationEntryPoint ())
653
+ .exceptionHandling()
654
+ .authenticationEntryPoint(new ApproovAuthenticationEntryPoint ())
622
655
.and()
623
- .antMatcher(" /forms" )
624
- .authorizeRequests()
625
- .antMatchers(HttpMethod . GET , " /forms" ). authenticated();
656
+ .antMatcher(" /forms" )
657
+ .authorizeRequests()
658
+ .antMatchers(HttpMethod . GET , " /forms" ). authenticated();
626
659
}
627
660
}
628
661
@@ -658,8 +691,7 @@ If we compare the initial implementation with the final result for the class
658
691
659
692
```java
660
693
-- - untitled (Previous )
661
- + -- - untitled (Previous )
662
- ++ + untitled (Current )
694
+ ++ + / home/ sublime/ workspace/ java/ spring/ src/ main/ java/ com/ criticalblue/ approov/ jwt/ WebSecurityConfig . java
663
695
@@ - 1 ,6 + 1 ,7 @@
664
696
package com.criticalblue.approov.jwt;
665
697
@@ -668,16 +700,16 @@ If we compare the initial implementation with the final result for the class
668
700
import org.springframework.security.config.annotation.web.builders. WebSecurity ;
669
701
import org.springframework.security.config.http. SessionCreationPolicy ;
670
702
import org.springframework.web.cors. CorsConfiguration ;
671
- @@ - 25 ,7 + 26 ,7 @@
703
+ @@ - 25 ,6 + 26 ,8 @@
672
704
CorsConfiguration configuration = new CorsConfiguration ();
673
705
configuration.setAllowedOrigins (Arrays .asList ("http :// localhost:5000"));
674
706
configuration .setAllowedMethods (Arrays .asList ("GET "));
675
- - configuration.addAllowedHeader ("Approov - Token ");
676
- + configuration.addAllowedHeader ("approov - token ");
707
+ + configuration.addAllowedHeader ("Authorization ");
708
+ + configuration.addAllowedHeader ("Approov - Token ");
677
709
UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource ();
678
710
source.registerCorsConfiguration ("/* *", configuration);
679
711
return source;
680
- @@ -36 ,27 +37 ,86 @@
712
+ @@ -35 ,27 +38 ,86 @@
681
713
web.ignoring().antMatchers("/error");
682
714
}
683
715
@@ -727,7 +759,8 @@ If we compare the initial implementation with the final result for the class
727
759
+ .authorizeRequests()
728
760
+ .antMatchers(HttpMethod.GET, "/shapes").authenticated();
729
761
+ }
730
- + }
762
+ }
763
+ -}
731
764
+
732
765
+ @Configuration
733
766
+ @Order(2)
@@ -750,12 +783,12 @@ If we compare the initial implementation with the final result for the class
750
783
+ .securityContext()
751
784
+ .securityContextRepository(new ApproovSecurityContextRepository(approovConfig, true))
752
785
+ .and()
753
- + .exceptionHandling()
754
- + .authenticationEntryPoint(new ApproovAuthenticationEntryPoint())
786
+ + .exceptionHandling()
787
+ + .authenticationEntryPoint(new ApproovAuthenticationEntryPoint())
755
788
+ .and()
756
- + .antMatcher("/forms")
757
- + .authorizeRequests()
758
- + .antMatchers(HttpMethod.GET, "/forms").authenticated();
789
+ + .antMatcher("/forms")
790
+ + .authorizeRequests()
791
+ + .antMatchers(HttpMethod.GET, "/forms").authenticated();
759
792
+ }
760
793
+ }
761
794
+
@@ -780,8 +813,8 @@ If we compare the initial implementation with the final result for the class
780
813
+ .authorizeRequests()
781
814
+ .antMatchers(HttpMethod.GET, "/**").permitAll();
782
815
+ }
783
- }
784
- }
816
+ + }
817
+ + }
785
818
```
786
819
787
820
0 commit comments