github.com/greenpau/go-authcrunch@v1.1.4/pkg/authn/handle_external_login.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 authn 16 17 import ( 18 "context" 19 "net/http" 20 "strings" 21 22 "github.com/greenpau/go-authcrunch/pkg/authn/enums/operator" 23 "github.com/greenpau/go-authcrunch/pkg/requests" 24 "go.uber.org/zap" 25 ) 26 27 func (p *Portal) handleHTTPExternalLogin(ctx context.Context, w http.ResponseWriter, r *http.Request, rr *requests.Request, authMethod string) error { 28 p.disableClientCache(w) 29 p.injectRedirectURL(ctx, w, r, rr) 30 31 if strings.Contains(r.URL.Path, "-js-callback") { 32 // Intercept callback with Javascript. 33 return p.handleJavascriptCallbackIntercept(ctx, w, r) 34 } 35 36 authRealm, err := getEndpoint(r.URL.Path, "/"+authMethod+"/") 37 if err != nil { 38 return p.handleHTTPError(ctx, w, r, rr, http.StatusBadRequest) 39 } 40 authRealm = strings.Split(authRealm, "/")[0] 41 42 rr.Upstream.Method = authMethod 43 rr.Upstream.Realm = authRealm 44 rr.Flags.Enabled = true 45 46 p.logger.Debug( 47 "External login requested", 48 zap.String("session_id", rr.Upstream.SessionID), 49 zap.String("request_id", rr.ID), 50 zap.String("base_url", rr.Upstream.BaseURL), 51 zap.String("base_path", rr.Upstream.BasePath), 52 zap.String("auth_method", rr.Upstream.Method), 53 zap.String("auth_realm", rr.Upstream.Realm), 54 zap.Any("request_path", r.URL.Path), 55 ) 56 57 provider := p.getIdentityProviderByRealm(authRealm) 58 if provider == nil { 59 p.logger.Warn( 60 "Authentication failed", 61 zap.String("session_id", rr.Upstream.SessionID), 62 zap.String("request_id", rr.ID), 63 zap.String("error", "identity provider not found"), 64 ) 65 return p.handleHTTPError(ctx, w, r, rr, http.StatusBadRequest) 66 } 67 err = provider.Request(operator.Authenticate, rr) 68 if err != nil { 69 p.logger.Warn( 70 "Authentication failed", 71 zap.String("session_id", rr.Upstream.SessionID), 72 zap.String("request_id", rr.ID), 73 zap.Error(err), 74 ) 75 return p.handleHTTPError(ctx, w, r, rr, http.StatusUnauthorized) 76 } 77 switch rr.Response.Code { 78 case http.StatusBadRequest: 79 return p.handleHTTPError(ctx, w, r, rr, http.StatusBadRequest) 80 case http.StatusOK: 81 p.logger.Info( 82 "Successful login", 83 zap.String("session_id", rr.Upstream.SessionID), 84 zap.String("request_id", rr.ID), 85 zap.String("auth_method", rr.Upstream.Method), 86 zap.String("auth_realm", rr.Upstream.Realm), 87 zap.Any("user", rr.Response.Payload), 88 ) 89 case http.StatusFound: 90 p.logger.Debug( 91 "Redirect to authorization server", 92 zap.String("session_id", rr.Upstream.SessionID), 93 zap.String("request_id", rr.ID), 94 zap.Any("url", rr.Response.RedirectURL), 95 ) 96 http.Redirect(w, r, rr.Response.RedirectURL, http.StatusFound) 97 return nil 98 default: 99 return p.handleHTTPError(ctx, w, r, rr, http.StatusNotImplemented) 100 } 101 // User authenticated successfully. 102 if err := p.authorizeLoginRequest(ctx, w, r, rr); err != nil { 103 return p.handleHTTPErrorWithLog(ctx, w, r, rr, rr.Response.Code, err.Error()) 104 } 105 w.WriteHeader(rr.Response.Code) 106 return nil 107 } 108 109 func (p *Portal) handleJavascriptCallbackIntercept(_ context.Context, w http.ResponseWriter, _ *http.Request) error { 110 p.disableClientCache(w) 111 w.WriteHeader(200) 112 113 w.Write([]byte(`<html> 114 <body> 115 <p>Redirecting to authentication endpoint.</p> 116 <script> 117 let redirectURL = window.location.href; 118 const i = redirectURL.indexOf("#"); 119 if (i < 0) { 120 redirectURL = redirectURL.replace('authorization-code-js-callback', 'authorization-code-callback'); 121 window.location = redirectURL; 122 } else { 123 redirectURI = redirectURL.slice(0, i).replace('authorization-code-js-callback', 'authorization-code-callback'); 124 window.location = redirectURI + "?" + redirectURL.slice(i+1); 125 } 126 </script> 127 </body> 128 </html>`)) 129 return nil 130 }