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