Add html screen for pkce flow (#1034)

* add html screen for pkce flow

* remove unused CSS classes in pkce-auth-msg.html

* remove links to external sources
This commit is contained in:
Bethuel Mmbaga 2023-07-28 19:10:12 +03:00 committed by GitHub
parent 24713fbe59
commit 64f6343fcc
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 136 additions and 20 deletions

View File

@ -6,6 +6,7 @@ import (
"crypto/subtle"
"encoding/base64"
"fmt"
"html/template"
"net"
"net/http"
"net/url"
@ -16,6 +17,7 @@ import (
"golang.org/x/oauth2"
"github.com/netbirdio/netbird/client/internal"
"github.com/netbirdio/netbird/client/internal/templates"
)
var _ OAuthFlow = &PKCEAuthorizationFlow{}
@ -136,33 +138,35 @@ func (p *PKCEAuthorizationFlow) startServer(tokenChan chan<- *oauth2.Token, errC
}()
http.HandleFunc("/", func(w http.ResponseWriter, req *http.Request) {
query := req.URL.Query()
tokenValidatorFunc := func() (*oauth2.Token, error) {
query := req.URL.Query()
state := query.Get(queryState)
// Prevent timing attacks on state
if subtle.ConstantTimeCompare([]byte(p.state), []byte(state)) == 0 {
errChan <- fmt.Errorf("invalid state")
return
state := query.Get(queryState)
// Prevent timing attacks on state
if subtle.ConstantTimeCompare([]byte(p.state), []byte(state)) == 0 {
return nil, fmt.Errorf("invalid state")
}
code := query.Get(queryCode)
if code == "" {
return nil, fmt.Errorf("missing code")
}
return p.oAuthConfig.Exchange(
req.Context(),
code,
oauth2.SetAuthURLParam("code_verifier", p.codeVerifier),
)
}
code := query.Get(queryCode)
if code == "" {
errChan <- fmt.Errorf("missing code")
return
}
// Exchange the authorization code for the OAuth token
token, err := p.oAuthConfig.Exchange(
req.Context(),
code,
oauth2.SetAuthURLParam("code_verifier", p.codeVerifier),
)
token, err := tokenValidatorFunc()
if err != nil {
errChan <- fmt.Errorf("OAuth token exchange failed: %v", err)
return
errChan <- fmt.Errorf("PKCE authorization flow failed: %v", err)
renderPKCEFlowTmpl(w, err)
}
tokenChan <- token
renderPKCEFlowTmpl(w, nil)
})
if err := server.ListenAndServe(); err != nil {
@ -215,3 +219,20 @@ func isRedirectURLPortUsed(redirectURL string) bool {
return true
}
func renderPKCEFlowTmpl(w http.ResponseWriter, authError error) {
tmpl, err := template.New("pkce-auth-flow").Parse(templates.PKCEAuthMsgTmpl)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
data := make(map[string]string)
if authError != nil {
data["Error"] = authError.Error()
}
if err := tmpl.Execute(w, data); err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
}
}

View File

@ -0,0 +1,8 @@
package templates
import (
_ "embed"
)
//go:embed pkce-auth-msg.html
var PKCEAuthMsgTmpl string

View File

@ -0,0 +1,87 @@
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1"/>
<style>
body {
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
margin: 0;
background: #f7f8f9;
font-family: sans-serif, Arial, Tahoma;
}
.container {
width: 100%;
background: white;
border: 1px solid #e8e9ea;
text-align: center;
padding: 20px;
padding-bottom: 50px;
max-width: 550px;
margin: 0 10px;
}
.logo {
height: 80px;
border-bottom: 1px solid #e8e9ea;
display: flex;
justify-content: center;
align-items: center;
}
.logo img {
width: 130px;
}
.content {
font-size: 13px;
color: #525252;
line-height: 18px;
padding: 10px 0;
}
.content div {
font-size: 18px;
line-height: normal;
margin-bottom: 5px;
color: black;
}
</style>
</head>
<body>
<div class="container">
<div class="logo">
<img src="https://img.mailinblue.com/6211297/images/content_library/original/64bd4ce82e1ea753e439b6a2.png">
</div>
<br>
{{ if .Error }}
<svg xmlns="http://www.w3.org/2000/svg" height="50" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" fill="none" stroke="red" stroke-width="3"/>
<path d="M30 30 L70 70 M30 70 L70 30" fill="none" stroke="red" stroke-width="3"/>
</svg>
<div class="content">
<div>
Login failed
</div>
{{ .Error }}.
</div>
{{ else }}
<svg xmlns="http://www.w3.org/2000/svg" height="50" viewBox="0 0 100 100">
<circle cx="50" cy="50" r="45" fill="none" stroke="#5cb85c" stroke-width="3"/>
<path d="M30 50 L45 65 L70 35" fill="none" stroke="#5cb85c" stroke-width="5"/>
</svg>
<div class="content">
<div>
Login successful
</div>
Your device is now registered and logged in to NetBird.
<br>
You can now close this window.
</div>
{{ end }}
</div>
</body>
</html>