|
|
@ -18,6 +18,9 @@ import ( |
|
|
|
"strconv" |
|
|
|
"strings" |
|
|
|
"time" |
|
|
|
|
|
|
|
"git.rootprojects.org/root/keypairs" |
|
|
|
"git.rootprojects.org/root/keypairs/keyfetch" |
|
|
|
) |
|
|
|
|
|
|
|
type PrivateJWK struct { |
|
|
@ -111,6 +114,112 @@ func Route(jwksPrefix string, priv *ecdsa.PrivateKey, jwk *PrivateJWK) { |
|
|
|
fmt.Fprintf(w, token) |
|
|
|
}) |
|
|
|
|
|
|
|
http.HandleFunc("/inspect_token", func(w http.ResponseWriter, r *http.Request) { |
|
|
|
token := r.Header.Get("Authorization") |
|
|
|
log.Printf("%s %s %s\n", r.Method, r.URL.Path, token) |
|
|
|
|
|
|
|
if "" == token { |
|
|
|
token = r.URL.Query().Get("access_token") |
|
|
|
if "" == token { |
|
|
|
http.Error(w, "Bad Format: missing Authorization header and 'access_token' query", http.StatusBadRequest) |
|
|
|
return |
|
|
|
} |
|
|
|
} else { |
|
|
|
parts := strings.Split(token, " ") |
|
|
|
if 2 != len(parts) { |
|
|
|
http.Error(w, "Bad Format: expected Authorization header to be in the format of 'Bearer <Token>'", http.StatusBadRequest) |
|
|
|
return |
|
|
|
} |
|
|
|
token = parts[1] |
|
|
|
} |
|
|
|
|
|
|
|
parts := strings.Split(token, ".") |
|
|
|
if 3 != len(parts) { |
|
|
|
http.Error(w, "Bad Format: token should be in the format of <protected-header>.<body>.<signature>", http.StatusBadRequest) |
|
|
|
return |
|
|
|
} |
|
|
|
protected64 := parts[0] |
|
|
|
data64 := parts[1] |
|
|
|
signature64 := parts[2] |
|
|
|
|
|
|
|
protectedB, err := base64.RawURLEncoding.DecodeString(protected64) |
|
|
|
if nil != err { |
|
|
|
http.Error(w, "Bad Format: token's header should be URL-safe base64 encoded", http.StatusBadRequest) |
|
|
|
return |
|
|
|
} |
|
|
|
dataB, err := base64.RawURLEncoding.DecodeString(data64) |
|
|
|
if nil != err { |
|
|
|
http.Error(w, "Bad Format: token's body should be URL-safe base64 encoded", http.StatusBadRequest) |
|
|
|
return |
|
|
|
} |
|
|
|
// TODO verify signature
|
|
|
|
_, err = base64.RawURLEncoding.DecodeString(signature64) |
|
|
|
if nil != err { |
|
|
|
http.Error(w, "Bad Format: token's signature should be URL-safe base64 encoded", http.StatusBadRequest) |
|
|
|
return |
|
|
|
} |
|
|
|
|
|
|
|
errors := []string{} |
|
|
|
|
|
|
|
protected := map[string]interface{}{} |
|
|
|
err = json.Unmarshal(protectedB, &protected) |
|
|
|
if nil != err { |
|
|
|
http.Error(w, "Bad Format: token's header should be URL-safe base64-encoded JSON", http.StatusBadRequest) |
|
|
|
return |
|
|
|
} |
|
|
|
kid, kidOK := protected["kid"].(string) |
|
|
|
// TODO parse jwkM
|
|
|
|
_, jwkOK := protected["jwk"] |
|
|
|
if !kidOK && !jwkOK { |
|
|
|
errors = append(errors, "must have either header.kid or header.jwk") |
|
|
|
} |
|
|
|
|
|
|
|
data := map[string]interface{}{} |
|
|
|
err = json.Unmarshal(dataB, &data) |
|
|
|
if nil != err { |
|
|
|
http.Error(w, "Bad Format: token's body should be URL-safe base64-encoded JSON", http.StatusBadRequest) |
|
|
|
return |
|
|
|
} |
|
|
|
iss, issOK := data["iss"].(string) |
|
|
|
if !jwkOK && !issOK { |
|
|
|
errors = append(errors, "body.iss must exist to complement header.kid") |
|
|
|
} |
|
|
|
|
|
|
|
pub, err := keyfetch.OIDCJWK(kid, iss) |
|
|
|
if nil != err { |
|
|
|
fmt.Println("couldn't fetch pub key:") |
|
|
|
fmt.Println(err) |
|
|
|
} |
|
|
|
fmt.Println("fetched pub key:") |
|
|
|
fmt.Println(pub) |
|
|
|
|
|
|
|
inspected := struct { |
|
|
|
Public keypairs.PublicKey `json:"public"` |
|
|
|
Protected map[string]interface{} `json:"protected"` |
|
|
|
Body map[string]interface{} `json:"body"` |
|
|
|
Signature string `json:"signature"` |
|
|
|
Verified bool `json:"verified"` |
|
|
|
Errors []string `json:"errors"` |
|
|
|
}{ |
|
|
|
Public: pub, |
|
|
|
Protected: protected, |
|
|
|
Body: data, |
|
|
|
Signature: signature64, |
|
|
|
Verified: false, |
|
|
|
Errors: errors, |
|
|
|
} |
|
|
|
|
|
|
|
tokenB, err := json.Marshal(inspected) |
|
|
|
if nil != err { |
|
|
|
fmt.Println("couldn't serialize inpsected token:") |
|
|
|
fmt.Println(err) |
|
|
|
} |
|
|
|
fmt.Println("serialized inpsected token") |
|
|
|
fmt.Println(inspected) |
|
|
|
fmt.Println(string(tokenB)) |
|
|
|
fmt.Fprintf(w, string(tokenB)) |
|
|
|
}) |
|
|
|
|
|
|
|
http.HandleFunc("/authorization_header", func(w http.ResponseWriter, r *http.Request) { |
|
|
|
log.Printf("%s %s\n", r.Method, r.URL.Path) |
|
|
|
|
|
|
@ -382,7 +491,7 @@ func GenToken(host string, priv *ecdsa.PrivateKey, query url.Values) (string, st |
|
|
|
sb = append([]byte{0}, sb...) |
|
|
|
} |
|
|
|
sig64 := base64.RawURLEncoding.EncodeToString(append(rb, sb...)) |
|
|
|
token := fmt.Sprintf(`%s.%s.%s`, protected64, payload64, sig64) |
|
|
|
token := fmt.Sprintf("%s.%s.%s\n", protected64, payload64, sig64) |
|
|
|
return protected, payload, token |
|
|
|
} |
|
|
|
|
|
|
|