@@ -60,18 +60,10 @@ public function refresh(array $claims) {
6060 if (time () < $ claims ['expires ' ]) return null ;
6161
6262 // Refresh token
63- $ result = $ this ->backend ->acquire ([
63+ return ByAccessToken:: from ( $ this ->backend ->acquire ([
6464 'grant_type ' => 'refresh_token ' ,
6565 'refresh_token ' => $ claims ['refresh ' ],
66- ]);
67- return new ByAccessToken (
68- $ result ['access_token ' ],
69- $ result ['token_type ' ] ?? 'Bearer ' ,
70- $ result ['scope ' ] ?? null ,
71- $ result ['expires_in ' ] ?? null ,
72- $ result ['refresh_token ' ] ?? null ,
73- $ result ['id_token ' ] ?? null
74- );
66+ ]));
7567 }
7668
7769 /**
@@ -84,36 +76,35 @@ public function refresh(array $claims) {
8476 * @throws lang.IllegalStateException
8577 */
8678 public function authenticate ($ request , $ response , $ session ) {
87- $ stored = $ session ->value ($ this ->namespace );
79+ $ stored = $ session ->value ($ this ->namespace ) ?? [ ' state ' => []] ;
8880
89- // We have an access token, reset state and return an authenticated session
90- // See https://www.oauth.com/oauth2-servers/access-tokens/access-token-response/
91- // and https://tools.ietf.org/html/rfc6749#section-5.1
81+ // We have an access token, remove and return an authenticated session. The
82+ // authentication implementation registers the user and transmits the session.
9283 if ($ token = $ stored ['token ' ] ?? null ) {
9384 unset($ stored ['token ' ]);
9485 $ session ->register ($ this ->namespace , $ stored );
9586
96- return new ByAccessToken (
97- $ token ['access_token ' ],
98- $ token ['token_type ' ] ?? 'Bearer ' ,
99- $ token ['scope ' ] ?? null ,
100- $ token ['expires_in ' ] ?? null ,
101- $ token ['refresh_token ' ] ?? null ,
102- $ token ['id_token ' ] ?? null
103- );
87+ return ByAccessToken::from ($ token );
10488 }
10589
90+ // Enter authentication flow, resolving callback URI against the curren request.
10691 $ uri = $ this ->url (true )->resolve ($ request );
10792 $ callback = $ this ->callback ? $ uri ->resolve ($ this ->callback ) : $ this ->service ($ uri );
10893
109- // Start authorization flow to acquire an access token
110- $ server = $ request ->param ('state ' );
111- if (null === $ server || null === $ stored ) {
94+ // Check whether we are continuing an existing authentication flow based on the
95+ // state given by the server and our session; or if we need to start a new one.
96+ if (null === ($ server = $ request ->param ('state ' ))) {
97+ $ flow = null ;
98+ } else {
99+ sscanf ($ server , self ::STATE , $ state , $ fragment );
100+ $ flow = $ this ->flow ($ state , $ stored );
101+ }
102+
103+ if (null === $ flow ) {
112104 $ state = bin2hex ($ this ->rand ->bytes (16 ));
113105 $ seed = $ this ->backend ->seed ();
114106
115- $ stored ??= ['flow ' => []];
116- $ stored ['flow ' ][$ state ]= ['uri ' => (string )$ uri , 'seed ' => $ seed ];
107+ $ stored ['flows ' ][$ state ]= ['uri ' => (string )$ uri , 'seed ' => $ seed ];
117108 $ session ->register ($ this ->namespace , $ stored );
118109 $ session ->transmit ($ response );
119110
@@ -128,59 +119,34 @@ public function authenticate($request, $response, $session) {
128119 $ target = $ this ->auth ->using ()->params ($ this ->backend ->pass ($ params , $ seed ))->create ();
129120
130121 // If a URL fragment is present, append it to the state parameter, which
131- // is passed as the last parameter to the authentication service.
132- $ this ->redirect ($ response , $ target , sprintf ('
133- var target = "%1$s";
134- var hash = document.location.hash.substring(1);
135-
122+ // is always passed as the last parameter to the authentication service.
123+ $ separator = self ::FRAGMENT ;
124+ return $ this ->redirect ($ response , $ target , <<<JS
125+ var hash = document.location.hash;
136126 if (hash) {
137- document.location.replace(target + "%2$s" + encodeURIComponent(hash));
127+ document.location.replace(' { $ target}{ $ separator } ' + encodeURIComponent(hash.substring(1) ));
138128 } else {
139- document.location.replace(target);
140- } ' ,
141- $ target ,
142- self ::FRAGMENT
143- ));
144- return null ;
145- }
129+ document.location.replace(' {$ target }');
130+ }
131+ JS
132+ );
133+ } else {
146134
147- // Continue authorization flow, handling previous session layout
148- $ state = explode (self ::FRAGMENT , $ server );
149- if (
150- ($ target = $ stored ['flow ' ][$ state [0 ]] ?? null ) ||
151- (($ target = $ stored ['target ' ] ?? null ) && ($ state [0 ] === $ stored ['state ' ]))
152- ) {
153- unset($ stored ['flow ' ][$ state [0 ]]);
154-
155- // Target is an array for old session layout and during transition
156- if (is_array ($ target )) {
157- $ uri = $ target ['uri ' ];
158- $ seed = $ target ['seed ' ];
159- } else {
160- $ uri = $ target ;
161- $ seed = [];
162- }
163-
164- // Exchange the auth code for an access token
135+ // Exchange the auth code for an access token, then remove the stored state.
165136 $ params = [
166137 'grant_type ' => 'authorization_code ' ,
167138 'code ' => $ request ->param ('code ' ),
168139 'redirect_uri ' => $ callback ,
169- 'state ' => $ server
140+ 'state ' => $ state
170141 ];
171- $ stored ['token ' ]= $ this ->backend ->acquire ($ params , $ seed );
142+ $ stored ['token ' ]= $ this ->backend ->acquire ($ params , $ flow ['seed ' ]);
143+
144+ unset($ stored ['flows ' ][$ state ], $ stored ['flow ' ][$ state ]);
172145 $ session ->register ($ this ->namespace , $ stored );
173146 $ session ->transmit ($ response );
174147
175148 // Redirect to self, using encoded fragment if present
176- $ this ->finalize ($ response , $ uri .(isset ($ state [1 ]) ? '# ' .urldecode ($ state [1 ]) : '' ));
177- return null ;
149+ return $ this ->finalize ($ response , $ flow ['uri ' ].(isset ($ fragment ) ? '# ' .urldecode ($ fragment ) : '' ));
178150 }
179-
180- throw new IllegalStateException (sprintf (
181- 'Flow error, unknown server state %s expecting one of %s ' ,
182- $ state [0 ],
183- implode (', ' , array_keys ($ stored ['flow ' ] ?? [$ stored ['state ' ] => true ]))
184- ));
185151 }
186152}
0 commit comments