Skip to content

Commit 494633a

Browse files
authored
feat: pretty print saml assertion to js console (#18)
1 parent 4cef571 commit 494633a

File tree

3 files changed

+88
-27
lines changed

3 files changed

+88
-27
lines changed

server/format.go

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
// Copyright Josh Komoroske. All rights reserved.
2+
// Use of this source code is governed by the MIT license,
3+
// a copy of which can be found in the LICENSE.txt file.
4+
// SPDX-License-Identifier: MIT
5+
6+
package server
7+
8+
import (
9+
"bytes"
10+
"encoding/base64"
11+
"encoding/xml"
12+
"io"
13+
)
14+
15+
// formatSAMLResponse takes a base64 encoded SAML assertion body, base64
16+
// decodes it, then pretty-prints the contained XML document.
17+
func formatSAMLResponse(raw string, writer io.Writer) error {
18+
decoder := xml.NewDecoder(base64.NewDecoder(base64.StdEncoding, bytes.NewReader([]byte(raw))))
19+
20+
encoder := xml.NewEncoder(writer)
21+
encoder.Indent("", " ")
22+
23+
// We have to round-trip through decoding+(re)encoding every token since it
24+
// isn't possible to unmarshal into an interface{}.
25+
for {
26+
token, err := decoder.RawToken()
27+
28+
if err == io.EOF {
29+
break
30+
}
31+
32+
if err != nil {
33+
return err
34+
}
35+
36+
if err := encoder.EncodeToken(token); err != nil {
37+
return err
38+
}
39+
}
40+
41+
if err := encoder.Flush(); err != nil {
42+
return err
43+
}
44+
45+
// Append a single newline character to improve with copying the formatted
46+
// XML body from the console.
47+
_, err := writer.Write([]byte("\n"))
48+
49+
return err
50+
}

server/server.go

Lines changed: 26 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,13 @@ import (
1919
// Returns a function that must be invoked by the caller to wait for the SAML
2020
// response and the server to shutdown.
2121
func Start(ctx context.Context, listen, url string) (string, func() (string, error)) { //nolint:cyclop,funlen
22-
// Channels for asynchronously communicating the SAML response string or
23-
// any errors that are encountered.
24-
responseChan := make(chan string)
22+
// Channels for asynchronously communicating any error that is encountered.
2523
errChan := make(chan error)
2624

25+
// The SAML response body that is received by the callback handler and
26+
// passed on to AWS.
27+
var samlResponse string
28+
2729
// sendError is a helper function for sending errors to the error channel
2830
// in a non-blocking fashion.
2931
sendError := func(err error) {
@@ -71,13 +73,32 @@ func Start(ctx context.Context, listen, url string) (string, func() (string, err
7173
}
7274

7375
go func() {
74-
// Write the SAML response string to our channel.
75-
responseChan <- request.FormValue("SAMLResponse")
76+
// Read the SAML response string.
77+
samlResponse = request.FormValue("SAMLResponse")
7678
}()
7779

7880
http.Redirect(writer, request, "/", http.StatusFound)
7981
})
8082

83+
// The /response route serves the formatted SAML assertion.
84+
mux.HandleFunc("/response", func(writer http.ResponseWriter, request *http.Request) {
85+
if request.Method != http.MethodGet {
86+
http.Error(writer, "method not allowed", http.StatusMethodNotAllowed)
87+
88+
return
89+
}
90+
91+
if len(samlResponse) == 0 {
92+
http.NotFound(writer, request)
93+
94+
return
95+
}
96+
97+
if err := formatSAMLResponse(samlResponse, writer); err != nil {
98+
http.Error(writer, err.Error(), http.StatusInternalServerError)
99+
}
100+
})
101+
81102
// The /callback route is called by the user to terminate this server.
82103
mux.HandleFunc("/shutdown", func(writer http.ResponseWriter, request *http.Request) {
83104
if request.Method != http.MethodPost {
@@ -114,12 +135,6 @@ func Start(ctx context.Context, listen, url string) (string, func() (string, err
114135
return fmt.Sprintf("http://%s/login", listen), func() (string, error) {
115136
defer server.Shutdown(ctx) //nolint:errcheck
116137

117-
var samlResponse string
118-
// Wait for the SAML response.
119-
go func() {
120-
samlResponse = <-responseChan
121-
}()
122-
123138
// Wait for an error.
124139
err := <-errChan
125140
switch err {

server/static/index.js

Lines changed: 12 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,18 +1,14 @@
11
"use strict";
22

3-
// ready calls the given function when the page is loaded and ready.
4-
function ready(fn) {
5-
if (document.readyState !== 'loading') {
6-
fn();
7-
} else {
8-
document.addEventListener('DOMContentLoaded', fn);
9-
}
10-
}
11-
12-
// shutdown sends a POST to the shutdown endpoint, signaling to the server to
13-
// terminate itself.
14-
function shutdown() {
15-
fetch('/shutdown', {method: 'POST'});
16-
}
17-
18-
ready(shutdown);
3+
window.addEventListener('load', () => {
4+
fetch('/response')
5+
.then(response => response.text()) // send response body to next then chain
6+
.then(body => {
7+
console.groupCollapsed("SAML Assertion");
8+
console.log(body);
9+
console.groupEnd();
10+
})
11+
.finally(() => {
12+
fetch('/shutdown', {method: 'POST'});
13+
});
14+
});

0 commit comments

Comments
 (0)