@@ -62,7 +62,7 @@ of this software and associated documentation files (the "Software"), to deal
6262import org .apache .http .client .CredentialsProvider ;
6363import org .apache .http .client .config .RequestConfig ;
6464import org .apache .http .client .methods .HttpPost ;
65- import org .apache .http .client .methods . HttpUriRequest ;
65+ import org .apache .http .client .utils . URIBuilder ;
6666import org .apache .http .impl .client .BasicCredentialsProvider ;
6767import org .apache .http .impl .client .CloseableHttpClient ;
6868import org .apache .http .impl .client .HttpClientBuilder ;
@@ -85,6 +85,8 @@ of this software and associated documentation files (the "Software"), to deal
8585import java .io .IOException ;
8686import java .net .InetSocketAddress ;
8787import java .net .Proxy ;
88+ import java .net .URISyntaxException ;
89+ import java .nio .charset .StandardCharsets ;
8890import java .security .SecureRandom ;
8991import java .util .Arrays ;
9092import java .util .HashSet ;
@@ -115,6 +117,8 @@ public class GithubSecurityRealm extends AbstractPasswordBasedSecurityRealm impl
115117 private Secret clientSecret ;
116118 private String oauthScopes ;
117119 private String [] myScopes ;
120+ @ NonNull
121+ private String redirectUri = "" ;
118122
119123 /**
120124 * @param githubWebUri The URI to the root of the web UI for GitHub or GitHub Enterprise,
@@ -187,6 +191,15 @@ private void setOauthScopes(String oauthScopes) {
187191 this .oauthScopes = oauthScopes ;
188192 }
189193
194+ /**
195+ * @param redirectUri the redirectUri to set
196+ */
197+ @ DataBoundSetter
198+ public void setRedirectUri (String redirectUri ) {
199+ if (null == redirectUri ) redirectUri = "" ;
200+ this .redirectUri = redirectUri ;
201+ }
202+
190203 /**
191204 * Checks the security realm for a GitHub OAuth scope.
192205 * @param scope A scope to check for in the security realm.
@@ -245,6 +258,10 @@ public void marshal(Object source, HierarchicalStreamWriter writer,
245258 writer .setValue (realm .getOauthScopes ());
246259 writer .endNode ();
247260
261+ writer .startNode ("redirectUri" );
262+ writer .setValue (realm .getRedirectUri ());
263+ writer .endNode ();
264+
248265 }
249266
250267 public Object unmarshal (HierarchicalStreamReader reader ,
@@ -274,8 +291,7 @@ public Object unmarshal(HierarchicalStreamReader reader,
274291 return realm ;
275292 }
276293
277- private void setValue (GithubSecurityRealm realm , String node ,
278- String value ) {
294+ private void setValue (GithubSecurityRealm realm , String node , String value ) {
279295 if (node .equalsIgnoreCase ("clientid" )) {
280296 realm .setClientID (value );
281297 } else if (node .equalsIgnoreCase ("clientsecret" )) {
@@ -290,6 +306,8 @@ private void setValue(GithubSecurityRealm realm, String node,
290306 realm .setGithubApiUri (value );
291307 } else if (node .equalsIgnoreCase ("oauthscopes" )) {
292308 realm .setOauthScopes (value );
309+ } else if (node .equalsIgnoreCase ("redirecturi" )) {
310+ realm .setRedirectUri (value );
293311 } else {
294312 throw new ConversionException ("Invalid node value = " + node );
295313 }
@@ -334,11 +352,21 @@ public String getOauthScopes() {
334352 return oauthScopes ;
335353 }
336354
355+ /**
356+ * @return the redirectUri
357+ */
358+ @ NonNull
359+ public String getRedirectUri () {
360+ return redirectUri ;
361+ }
362+
337363 public HttpResponse doCommenceLogin (StaplerRequest request , @ QueryParameter String from , @ Header ("Referer" ) final String referer )
338- throws IOException {
364+ throws IOException , URISyntaxException {
339365 // https://tools.ietf.org/html/rfc6749#section-10.10 dictates that probability that an attacker guesses the string
340366 // SHOULD be less than or equal to 2^(-160) and our Strings consist of 65 chars. (65^27 ~= 2^160)
341367 final String state = getSecureRandomString (27 );
368+
369+ // This is to go back to the current page after login, not the oauth callback
342370 String redirectOnFinish ;
343371 if (from != null && Util .isSafeToRedirectTo (from )) {
344372 redirectOnFinish = from ;
@@ -355,17 +383,17 @@ public HttpResponse doCommenceLogin(StaplerRequest request, @QueryParameter Stri
355383 for (GitHubOAuthScope s : Jenkins .get ().getExtensionList (GitHubOAuthScope .class )) {
356384 scopes .addAll (s .getScopesToRequest ());
357385 }
358- String suffix ="" ;
359- if (!scopes .isEmpty ()) {
360- suffix = "&scope=" +Util .join (scopes ,"," )+"&state=" +state ;
361- } else {
362- // We need repo scope in order to access private repos
363- // See https://developer.github.com/v3/oauth/#scopes
364- suffix = "&scope=" + oauthScopes +"&state=" +state ;
386+
387+ URIBuilder builder = new URIBuilder (githubWebUri , StandardCharsets .UTF_8 );
388+ builder .setPath ("/login/oauth/authorize" );
389+ builder .setParameter ("client_id" , clientID );
390+ builder .setParameter ("scope" , scopes .isEmpty () ? oauthScopes : Util .join (scopes ,"," ));
391+ builder .setParameter ("state" , state );
392+ if (!redirectUri .isEmpty ()) {
393+ builder .setParameter ("redirect_uri" , redirectUri );
365394 }
366395
367- return new HttpRedirect (githubWebUri + "/login/oauth/authorize?client_id="
368- + clientID + suffix );
396+ return new HttpRedirect (builder .toString ());
369397 }
370398
371399 /**
@@ -630,6 +658,12 @@ public String getDefaultOauthScopes() {
630658 return DEFAULT_OAUTH_SCOPES ;
631659 }
632660
661+ public String getDefaultRequestUri () {
662+ // Intentionally making this default in UI and not in code
663+ // to preserve behaviour for existing groovy init & JCasc setups
664+ return Jenkins .get ().getRootUrl () + "securityRealm/finishLogin" ;
665+ }
666+
633667 public DescriptorImpl () {
634668 super ();
635669 // TODO Auto-generated constructor stub
@@ -728,7 +762,8 @@ public boolean equals(Object object){
728762 this .getGithubApiUri ().equals (obj .getGithubApiUri ()) &&
729763 this .getClientID ().equals (obj .getClientID ()) &&
730764 this .getClientSecret ().equals (obj .getClientSecret ()) &&
731- this .getOauthScopes ().equals (obj .getOauthScopes ());
765+ this .getOauthScopes ().equals (obj .getOauthScopes ()) &&
766+ this .getRedirectUri ().equals (obj .getRedirectUri ());
732767 } else {
733768 return false ;
734769 }
@@ -742,6 +777,7 @@ public int hashCode() {
742777 .append (this .getClientID ())
743778 .append (this .getClientSecret ())
744779 .append (this .getOauthScopes ())
780+ .append (this .getRedirectUri ())
745781 .toHashCode ();
746782 }
747783
0 commit comments