3737import java .security .SecureRandom ;
3838import java .util .Base64 ;
3939import java .util .concurrent .CompletableFuture ;
40+ import java .util .concurrent .ExecutionException ;
41+ import java .util .concurrent .TimeUnit ;
42+ import java .util .concurrent .TimeoutException ;
4043import java .util .concurrent .atomic .AtomicReference ;
4144import java .awt .Desktop ;
4245import java .awt .Desktop .Action ;
@@ -61,31 +64,38 @@ OAuth2Token retrieveToken() throws IOException {
6164 try {
6265 byte [] verifierBytes = new byte [128 ];
6366 SecureRandom .getInstanceStrong ().nextBytes (verifierBytes );
64- Base64 .Encoder b64encoder = Base64 .getUrlEncoder ();
67+ Base64 .Encoder b64encoder = Base64 .getUrlEncoder (). withoutPadding () ;
6568 final String verifier = b64encoder .encodeToString (verifierBytes );
6669
6770 MessageDigest md = MessageDigest .getInstance ("SHA-256" );
6871 final String challenge = b64encoder .encodeToString (md .digest (verifierBytes ));
6972 HttpServer server = HttpServer .create (new InetSocketAddress ("localhost" , 0 ), 0 );
7073 int port = server .getAddress ().getPort ();
7174 String host = server .getAddress ().getHostName ();
72- final AtomicReference <String > code = new AtomicReference <>();
73- final AtomicReference <String > state = new AtomicReference <>();
74- final CompletableFuture <Void > future = new CompletableFuture <>();
75+
76+ final CompletableFuture <Result > future = new CompletableFuture <>();
7577
7678 server .createContext ("/" , new HttpHandler () {
7779
7880 @ Override
7981 public void handle (HttpExchange exchange ) throws IOException {
82+ Result ret = null ;
83+
8084 final String query = exchange .getRequestURI ().getQuery ();
85+ System .out .println ("Got auth server response." + query );
8186 final QueryParameters parameters = QueryParameters .parse (query );
82-
83- code .set (parameters .get ("code" ).get (0 ));
84- state .set (parameters .get ("state" ).get (0 ));
85- exchange .sendResponseHeaders (201 , 0 );
86- System .out .println ("Got code" );
87+ if (!parameters .get ("error" ).isEmpty ()) {
88+ String error = parameters .get ("error" ).get (0 );
89+ String errorDescription = parameters .get ("error_description" ).get (0 );
90+ ret = Result .failure (error , errorDescription );
91+ } else {
92+ String code = parameters .get ("code" ).get (0 );
93+ String state = parameters .get ("state" ).get (0 );
94+ ret = Result .success (code ,state );
95+ }
96+ System .out .println ("Finishing" );
8797 server .stop (0 );
88- future .complete (null );
98+ future .complete (ret );
8999 }
90100
91101 });
@@ -94,26 +104,30 @@ public void handle(HttpExchange exchange) throws IOException {
94104 .set ("grant_type" , "code" )
95105 .set ("client_id" , getClientId ())
96106 .set ("scopes" , "openid profile" )
107+ .set ("response_type" , "code" )
97108 .set ("code_challenge_method" , "S256" )
98109 .set ("code_challenge" , challenge )
99- .set ("redirect_uri" , redirectUri );
110+ .set ("redirect_uri" , redirectUri )
111+ .set ("kc_idp_hint" , "login.gov" );
100112 String urlStr = String .format ("%s?%s" , getAuthUrl ().getApiRoot (), authParameters .encode ());
101113 // start server to listen
102114 server .start ();
115+ System .out .println (urlStr );
103116 this .authCallBack .accept (URI .create (urlStr ));
104117
105-
106- future .join ();
118+ Result result = future .get (1 , TimeUnit .MINUTES ); // The user is now required to perform manual operations.
107119 System .out .println ("Next steps." );
108-
120+ if (result .error != null ) {
121+ throw new IOException (String .format ("Unable to login. %s : %s" , result .error , result .errorDescription ));
122+ }
109123 final UrlEncodedFormData formData = new UrlEncodedFormData ();
110124 formData .addClientId (getClientId ())
111125 .addGrantType ("authorization_code" )
112126 .addParameter ("code_verifier" , verifier )
113127 .addScopes ("openid" , "profile" )
114128 .addParameter ("redirect_uri" , redirectUri )
115- .addParameter ("session_state" , state . get () )
116- .addParameter ("code" , code . get () )
129+ .addParameter ("session_state" , result . state )
130+ .addParameter ("code" , result . code )
117131 .addParameter ("response_mode" , "fragment" )
118132 .addParameter ("response_type" , "id_token token" );
119133
@@ -131,6 +145,31 @@ public void handle(HttpExchange exchange) throws IOException {
131145 return retVal ;
132146 } catch (NoSuchAlgorithmException ex ) {
133147 throw new IOException ("Unable to retrieve SecureRandom or Message Digest instance to generate verifier" , ex );
148+ } catch (InterruptedException | ExecutionException | TimeoutException ex ) {
149+ throw new IOException ("Unable to form login sequence." , ex );
134150 }
135151 }
152+
153+ private static class Result {
154+ public final String code ;
155+ public final String state ;
156+
157+ public final String error ;
158+ public final String errorDescription ;
159+
160+ private Result (String code , String state , String error , String errorDescription ) {
161+ this .code = code ;
162+ this .state = state ;
163+ this .error = error ;
164+ this .errorDescription = errorDescription ;
165+ }
166+
167+ public static Result success (String code , String state ) {
168+ return new Result (code , state , null , null );
169+ }
170+
171+ public static Result failure (String error , String errorDescription ) {
172+ return new Result (null , null , error , errorDescription );
173+ }
174+ };
136175}
0 commit comments