github.com/greenpau/go-authcrunch@v1.1.4/pkg/authz/handlers/redirect.go (about) 1 // Copyright 2022 Paul Greenberg greenpau@outlook.com 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package handlers 16 17 import ( 18 "fmt" 19 "github.com/greenpau/go-authcrunch/pkg/requests" 20 addrutil "github.com/greenpau/go-authcrunch/pkg/util/addr" 21 "html/template" 22 "net/http" 23 "net/url" 24 "strings" 25 ) 26 27 var jsRedirTmpl = template.Must(template.New("js_redir").Parse(` 28 <html> 29 <body> 30 <p>User Unauthorized. Redirecting to login.</p> 31 <script> 32 var auth_url_path = "{{.AuthURLPath}}"; 33 var sep = "{{.Sep}}"; 34 var redir_param = "{{.RedirParam}}"; 35 var redir_url = "{{.RedirURL}}"; 36 if (window.location.hash) { 37 redir_url = redir_url + "#" + window.location.hash.substr(1); 38 } 39 var final_url = auth_url_path; 40 if (redir_param) { 41 final_url = auth_url_path + sep + redir_param + "=" + encodeURIComponent(redir_url); 42 } 43 window.location = final_url; 44 </script> 45 </body> 46 </html> 47 `)) 48 49 // HandleLocationHeaderRedirect redirects the requests to configured auth URL 50 // by setting Location header and sending 302. 51 func HandleLocationHeaderRedirect(w http.ResponseWriter, r *http.Request, rr *requests.AuthorizationRequest) { 52 configureRedirect(w, r, rr) 53 if !rr.Redirect.Enabled { 54 return 55 } 56 57 if rr.Redirect.QueryDisabled { 58 w.Header().Set("Location", rr.Redirect.AuthURL) 59 } else { 60 var sb strings.Builder 61 sb.WriteString(rr.Redirect.AuthURL) 62 sb.WriteString(rr.Redirect.Separator) 63 sb.WriteString(rr.Redirect.QueryParameter) 64 sb.WriteString("=") 65 sb.WriteString(url.QueryEscape(rr.Redirect.URL)) 66 w.Header().Set("Location", sb.String()) 67 } 68 69 if rr.Redirect.StatusCode == 0 { 70 rr.Redirect.StatusCode = 302 71 } 72 73 w.WriteHeader(rr.Redirect.StatusCode) 74 w.Write([]byte(http.StatusText(rr.Redirect.StatusCode))) 75 return 76 } 77 78 // HandleJavascriptRedirect redirects the requests to configured auth URL by 79 // responding Javascript-enabled HTML performing script-based redirection. 80 func HandleJavascriptRedirect(w http.ResponseWriter, r *http.Request, rr *requests.AuthorizationRequest) { 81 configureRedirect(w, r, rr) 82 if !rr.Redirect.Enabled { 83 return 84 } 85 86 if rr.Redirect.StatusCode == 0 { 87 rr.Redirect.StatusCode = 401 88 } 89 90 w.WriteHeader(rr.Redirect.StatusCode) 91 jsRedirTmpl.Execute(w, map[string]string{ 92 "AuthURLPath": rr.Redirect.AuthURL, 93 "Sep": rr.Redirect.Separator, 94 "RedirParam": rr.Redirect.QueryParameter, 95 "RedirURL": rr.Redirect.URL, 96 }) 97 return 98 } 99 100 func configureRedirect(w http.ResponseWriter, r *http.Request, rr *requests.AuthorizationRequest) { 101 if strings.Contains(r.RequestURI, rr.Redirect.QueryParameter) { 102 rr.Redirect.Enabled = false 103 return 104 } 105 106 rr.Redirect.Enabled = true 107 108 if rr.Redirect.QueryDisabled { 109 return 110 } 111 112 if strings.HasPrefix(r.RequestURI, "/") { 113 u, err := addrutil.GetCurrentURLWithSuffix(r, "") 114 if err != nil { 115 return 116 } 117 rr.Redirect.URL = u 118 } else { 119 rr.Redirect.URL = r.RequestURI 120 } 121 122 rr.Redirect.Separator = "?" 123 124 if strings.Contains(rr.Redirect.AuthURL, "?") { 125 rr.Redirect.Separator = "&" 126 } 127 128 if len(rr.Redirect.LoginHint) > 0 { 129 loginHint := rr.Redirect.LoginHint 130 escapedLoginHint := url.QueryEscape(loginHint) 131 rr.Redirect.AuthURL = fmt.Sprintf("%s%slogin_hint=%s", rr.Redirect.AuthURL, rr.Redirect.Separator, escapedLoginHint) 132 rr.Redirect.Separator = "&" 133 } 134 135 if len(rr.Redirect.AdditionalScopes) > 0 { 136 additionalScopes := rr.Redirect.AdditionalScopes 137 escapedAdditionalScopes := url.QueryEscape(additionalScopes) 138 rr.Redirect.AuthURL = fmt.Sprintf("%s%sadditional_scopes=%s", rr.Redirect.AuthURL, rr.Redirect.Separator, escapedAdditionalScopes) 139 rr.Redirect.Separator = "&" 140 } 141 142 return 143 }