github.com/greenpau/go-authcrunch@v1.1.4/pkg/authn/handle_http_logout.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/redirects"
    20  	"github.com/greenpau/go-authcrunch/pkg/requests"
    21  	"github.com/greenpau/go-authcrunch/pkg/user"
    22  	addrutil "github.com/greenpau/go-authcrunch/pkg/util/addr"
    23  	"go.uber.org/zap"
    24  	"net/http"
    25  	"net/url"
    26  	"strings"
    27  )
    28  
    29  func (p *Portal) deleteAuthCookies(w http.ResponseWriter, r *http.Request) {
    30  	for tokenName := range p.validator.GetAuthCookies() {
    31  		w.Header().Add("Set-Cookie", p.cookie.GetDeleteCookie(addrutil.GetSourceHost(r), tokenName))
    32  	}
    33  }
    34  
    35  func (p *Portal) handleHTTPLogout(ctx context.Context, w http.ResponseWriter, r *http.Request, rr *requests.Request, parsedUser *user.User) error {
    36  	p.disableClientCache(w)
    37  	p.injectRedirectURL(ctx, w, r, rr)
    38  	h := addrutil.GetSourceHost(r)
    39  	for tokenName := range p.validator.GetAuthCookies() {
    40  		w.Header().Add("Set-Cookie", p.cookie.GetDeleteCookie(h, tokenName))
    41  	}
    42  	w.Header().Add("Set-Cookie", p.cookie.GetDeleteCookie(h, p.cookie.Referer))
    43  	w.Header().Add("Set-Cookie", p.cookie.GetDeleteCookie(h, p.cookie.SessionID))
    44  
    45  	if parsedUser != nil && parsedUser.Claims != nil {
    46  		p.logger.Debug(
    47  			"user logout",
    48  			zap.String("session_id", rr.Upstream.SessionID),
    49  			zap.String("request_id", rr.ID),
    50  			zap.Any("user", parsedUser.Claims),
    51  		)
    52  		if strings.Contains(parsedUser.Claims.Issuer, "/oauth2/") {
    53  			return p.handleHTTPRedirect(ctx, w, r, rr, extractRealmLogout(parsedUser.Claims.Issuer, "oauth2"))
    54  		}
    55  		// The user is authenticated. Find whether there is redirect_uri present in Query.
    56  		if redirects.HasRedirectURI(r.URL) && (len(p.config.TrustedLogoutRedirectURIConfigs) > 0) {
    57  			p.logger.Debug(
    58  				"user logout with redirect",
    59  				zap.String("session_id", rr.Upstream.SessionID),
    60  				zap.String("request_id", rr.ID),
    61  				zap.Any("user", parsedUser.Claims),
    62  			)
    63  			redirectURI := redirects.GetRedirectURI(r.URL)
    64  			if redirectURI != nil {
    65  				if redirects.Match(redirectURI, p.config.TrustedLogoutRedirectURIConfigs) {
    66  					p.logger.Debug(
    67  						"found trusted logout redirect uri",
    68  						zap.String("session_id", rr.Upstream.SessionID),
    69  						zap.String("request_id", rr.ID),
    70  						zap.String("redirect_uri", redirects.GetRawRedirectURI(r.URL)),
    71  					)
    72  					return p.handleHTTPRedirectExternal(ctx, w, r, rr, redirects.GetRawRedirectURI(r.URL))
    73  				}
    74  			}
    75  		}
    76  	} else {
    77  		p.logger.Debug(
    78  			"user logout",
    79  			zap.String("session_id", rr.Upstream.SessionID),
    80  			zap.String("request_id", rr.ID),
    81  		)
    82  	}
    83  
    84  	return p.handleHTTPRedirect(ctx, w, r, rr, "/login")
    85  }
    86  
    87  func (p *Portal) handleHTTPLogoutWithLocalRedirect(ctx context.Context, w http.ResponseWriter, r *http.Request, rr *requests.Request) error {
    88  	var refererExists bool
    89  	p.disableClientCache(w)
    90  	p.injectRedirectURL(ctx, w, r, rr)
    91  	h := addrutil.GetSourceHost(r)
    92  	for tokenName := range p.validator.GetAuthCookies() {
    93  		w.Header().Add("Set-Cookie", p.cookie.GetDeleteCookie(h, tokenName))
    94  	}
    95  	if rr.Response.RedirectURL == "" {
    96  		w.Header().Add("Set-Cookie", p.cookie.GetDeleteCookie(h, p.cookie.Referer))
    97  	}
    98  	w.Header().Add("Set-Cookie", p.cookie.GetDeleteCookie(h, p.cookie.SessionID))
    99  	// The redirect_url query parameter exists.
   100  	if rr.Response.RedirectURL != "" {
   101  		return p.handleHTTPRedirect(ctx, w, r, rr, "/login?redirect_url="+rr.Response.RedirectURL)
   102  	}
   103  	// Find whether the redirect cookie exists. If so, do not inject redirect URL.
   104  	if cookie, err := r.Cookie(p.cookie.Referer); err == nil {
   105  		v, err := url.Parse(cookie.Value)
   106  		if err == nil && v.String() != "" {
   107  			refererExists = true
   108  		}
   109  	}
   110  	if !refererExists {
   111  		w.Header().Add("Set-Cookie", p.cookie.GetDeleteCookie(h, p.cookie.Referer))
   112  		return p.handleHTTPRedirect(ctx, w, r, rr, "/login?redirect_url="+r.RequestURI)
   113  	}
   114  	return p.handleHTTPRedirect(ctx, w, r, rr, "/login")
   115  }
   116  
   117  func extractRealmLogout(s, sp string) string {
   118  	var ready bool
   119  	for _, k := range strings.Split(s, "/") {
   120  		if k == sp {
   121  			ready = true
   122  			continue
   123  		}
   124  		if ready {
   125  			return "/" + strings.Join([]string{sp, k, "logout"}, "/")
   126  		}
   127  	}
   128  	return "/logout"
   129  }