29
29
import javax .ejb .Timer ;
30
30
import javax .ejb .TimerConfig ;
31
31
import javax .ejb .TimerService ;
32
- import java .util .Date ;
32
+ import java .util .concurrent . TimeUnit ;
33
33
import java .util .logging .Level ;
34
34
import java .util .logging .Logger ;
35
35
@@ -48,9 +48,12 @@ public class OneTimeJWTRotation {
48
48
@ PostConstruct
49
49
public void init () {
50
50
//number of milliseconds that must elapse between timer expiration notifications
51
- long intervalDuration = 24 *3600000L ; // 24 hour
52
- timer = timerService .createIntervalTimer (0 , intervalDuration , new TimerConfig ("Mark old JWT signing keys for " +
53
- "deletion" , false ));
51
+ long intervalDuration = TimeUnit .HOURS .toMillis (24 );
52
+
53
+ // The Info of the Timer **MUST** start with 'Mark'
54
+ // Read comment of the @Timeout annotated method below
55
+ timer = timerService .createIntervalTimer (0 , intervalDuration , new TimerConfig ("Mark old one-time JWT signing key" ,
56
+ false ));
54
57
}
55
58
56
59
@ PreDestroy
@@ -60,29 +63,52 @@ public void destroy() {
60
63
}
61
64
}
62
65
66
+ /**
67
+ * Recurring Timer method which rotates the one-time JWT signing key.
68
+ * It will rename the current to *_old and generate a new one
69
+ * During validation of a JWT we lookup the signing key based on the ID,
70
+ * so even if the name changes we will still be able to validate "older" JWTs
71
+ *
72
+ * Once we have generated a new signing key and renamed the old we create
73
+ * a single action Timer which fires a little bit later. The reason is that we
74
+ * want sessions which are using the old signing key to still be able to authenticate
75
+ * for a short-while.
76
+ *
77
+ * Since we cannot have two methods in the same EJB with @Timeout annotation we take
78
+ * different paths in the same method based on the **Info** field of the Timer.
79
+ * The recurring Timer's Info will/must start with 'Mark' while the single action
80
+ * will/must start with 'Sweep'
81
+ */
63
82
@ Timeout
64
- public void markOldSigningKeys ( ) {
83
+ public void rotateOneTimeJWTSigningKey ( Timer timer ) {
65
84
if (!payaraClusterManager .amIThePrimary ()) {
85
+ LOGGER .log (Level .INFO , "I am not the Primary or Active region. Skip rotating one-time JWT signing key" );
66
86
return ;
67
87
}
68
- boolean marked = jWTController .markOldSigningKeys ();
69
- if (marked ) {
70
- //(60000 + 60000)*2 = 240000 milliseconds = 4 min
71
- long duration = (Constants .DEFAULT_EXPIRY_LEEWAY * 1000 + Constants .ONE_TIME_JWT_LIFETIME_MS ) * 2 ;
72
- TimerConfig config = new TimerConfig ();
73
- config .setInfo ("Remove old JWT signing keys" );
74
- config .setPersistent (false );
75
- timerService .createSingleActionTimer (duration , config );
76
- }
77
- }
78
88
79
- @ Timeout
80
- public void performTimeout (Timer timer ) {
81
- try {
82
- jWTController .removeMarkedKeys ();
83
- LOGGER .log (Level .INFO , "{0} timer event: {1}." , new Object []{timer .getInfo (), new Date ()});
84
- } catch (Exception e ) {
85
- LOGGER .log (Level .SEVERE , "Got an exception while rotating one-time jwt" , e );
89
+ String timerInfo = (String ) timer .getInfo ();
90
+ if (timerInfo .startsWith ("Mark" )) {
91
+ LOGGER .log (Level .INFO , "Rotating one-time JWT signing key" );
92
+ boolean marked = jWTController .markOldSigningKeys ();
93
+ if (marked ) {
94
+ LOGGER .log (Level .INFO , "Marked old one-time JWT signing key, scheduling Sweeper" );
95
+ //(60000 + 60000)*2 = 240000 milliseconds = 4 min
96
+ long duration = (Constants .DEFAULT_EXPIRY_LEEWAY * 1000 + Constants .ONE_TIME_JWT_LIFETIME_MS ) * 2 ;
97
+ TimerConfig config = new TimerConfig ();
98
+ // The Info of the Timer **MUST** start with 'Sweep'
99
+ // Read comment of the method above
100
+ config .setInfo ("Sweep old one-time JWT signing key" );
101
+ config .setPersistent (false );
102
+ timerService .createSingleActionTimer (duration , config );
103
+ }
104
+ } else if (timerInfo .startsWith ("Sweep" )) {
105
+ LOGGER .log (Level .INFO , "Sweeping old one-time JWT signing key" );
106
+ try {
107
+ jWTController .removeMarkedKeys ();
108
+ LOGGER .log (Level .INFO , "Deleted old one-time JWT signing key" );
109
+ } catch (Exception e ) {
110
+ LOGGER .log (Level .SEVERE , "Failed to delete old one-time JWT signing key" , e );
111
+ }
86
112
}
87
113
}
88
114
}
0 commit comments