add endpoint to get emails

This commit is contained in:
AJ ONeal 2020-09-16 23:19:38 +00:00
parent f13dc593b0
commit 881bf97334
1 changed files with 166 additions and 72 deletions

View File

@ -283,7 +283,7 @@ func Route(jwksPrefix string, privkey keypairs.PrivateKey) http.Handler {
xkeypairs.Object{},
xkeypairs.Object{
"sub": sub,
"iss": baseURL,
"iss": baseURL + "/",
"jti": base64.RawURLEncoding.EncodeToString(nonce),
"iat": time.Now().Unix(),
"exp": "1h",
@ -302,6 +302,84 @@ func Route(jwksPrefix string, privkey keypairs.PrivateKey) http.Handler {
w.Write(b)
})
http.HandleFunc("/api/authz/email", 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 {
http.Error(
w,
"Bad Format: missing Authorization header and 'access_token' query",
http.StatusBadRequest,
)
return
}
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]
inspected, err := verifyToken(token)
if nil != err {
http.Error(w, "Invalid Token: "+err.Error(), http.StatusBadRequest)
return
}
if !inspected.Verified {
http.Error(w, "bad token signature", http.StatusBadRequest)
return
}
iss, _ := inspected.Payload["iss"].(string)
baseURL := getBaseURL(r)
if baseURL != strings.TrimSuffix(iss, "/") {
http.Error(w, "unacceptable token issuer", http.StatusBadRequest)
return
}
subject, _ := inspected.Payload["sub"].(string)
value, ok, err := subjectKV.Load(subject, &Subject{})
if nil != err {
http.Error(w, "Server Error: "+err.Error(),
http.StatusInternalServerError,
)
return
}
if !ok {
http.Error(w, "Server Error: the user went missing... huh",
http.StatusInternalServerError,
)
return
}
sub, ok := value.(*Subject)
if !ok {
http.Error(
w,
"Server Error: invalid data type",
http.StatusInternalServerError,
)
return
}
var emails []string
for k := range sub.Emails {
emails = append(emails, k)
}
jsonb, _ := json.MarshalIndent(struct {
Emails []string `json:"emails"`
}{
Emails: emails,
}, "", " ")
fmt.Fprintf(w, string(jsonb)+"\n")
})
http.HandleFunc("/api/new-nonce", requireHashcash(func(w http.ResponseWriter, r *http.Request) {
indexURL := getBaseURL(r) + "/api/directory"
w.Header().Set("Link", "<"+indexURL+">;rel=\"index\"")
@ -501,93 +579,39 @@ func Route(jwksPrefix string, privkey keypairs.PrivateKey) http.Handler {
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)
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)
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>.<payload>.<signature>", http.StatusBadRequest)
return
}
protected64 := parts[0]
payload64 := parts[1]
signature64 := parts[2]
protectedB, err := base64.RawURLEncoding.DecodeString(protected64)
inspected, err := verifyToken(token)
if nil != err {
http.Error(w, "Bad Format: token's header should be URL-safe base64 encoded", http.StatusBadRequest)
http.Error(w, "Invalid Token: "+err.Error(), http.StatusBadRequest)
return
}
payloadB, err := base64.RawURLEncoding.DecodeString(payload64)
if nil != err {
http.Error(w, "Bad Format: token's payload should be URL-safe base64 encoded", http.StatusBadRequest)
return
}
// TODO verify signature
sig, 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(payloadB, &data)
if nil != err {
http.Error(w, "Bad Format: token's payload should be URL-safe base64-encoded JSON", http.StatusBadRequest)
return
}
iss, issOK := data["iss"].(string)
if !jwkOK && !issOK {
errors = append(errors, "payload.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)
hash := sha256.Sum256([]byte(fmt.Sprintf("%s.%s", protected64, payload64)))
verified := JOSEVerify(pub, hash[:], sig)
inspected := &InspectableToken{
Public: pub,
Protected: protected,
Payload: data,
Signature: signature64,
Verified: verified,
Errors: errors,
}
tokenB, _ := json.MarshalIndent(inspected, "", " ")
if nil != err {
http.Error(w, "Bad Format: malformed token, or malformed jwk at issuer url", http.StatusInternalServerError)
http.Error(
w,
"Bad Format: malformed token, or malformed jwk at issuer url",
http.StatusInternalServerError,
)
return
}
fmt.Fprintf(w, string(tokenB)+"\n")
@ -680,6 +704,76 @@ func Route(jwksPrefix string, privkey keypairs.PrivateKey) http.Handler {
return http.DefaultServeMux
}
func verifyToken(token string) (*InspectableToken, error) {
parts := strings.Split(token, ".")
if 3 != len(parts) {
return nil, errors.New("Bad Format: token should be in the format of <protected-header>.<payload>.<signature>")
}
protected64 := parts[0]
payload64 := parts[1]
signature64 := parts[2]
protectedB, err := base64.RawURLEncoding.DecodeString(protected64)
if nil != err {
return nil, errors.New("Bad Format: token's header should be URL-safe base64 encoded")
}
payloadB, err := base64.RawURLEncoding.DecodeString(payload64)
if nil != err {
return nil, errors.New("Bad Format: token's payload should be URL-safe base64 encoded")
}
// TODO verify signature
sig, err := base64.RawURLEncoding.DecodeString(signature64)
if nil != err {
return nil, errors.New("Bad Format: token's signature should be URL-safe base64 encoded")
}
errs := []string{}
protected := map[string]interface{}{}
err = json.Unmarshal(protectedB, &protected)
if nil != err {
return nil, errors.New("Bad Format: token's header should be URL-safe base64-encoded JSON")
}
kid, kidOK := protected["kid"].(string)
// TODO parse jwkM
_, jwkOK := protected["jwk"]
if !kidOK && !jwkOK {
errs = append(errs, "must have either header.kid or header.jwk")
}
data := map[string]interface{}{}
err = json.Unmarshal(payloadB, &data)
if nil != err {
return nil, errors.New("Bad Format: token's payload should be URL-safe base64-encoded JSON")
}
iss, issOK := data["iss"].(string)
if !jwkOK && !issOK {
errs = append(errs, "payload.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)
hash := sha256.Sum256([]byte(fmt.Sprintf("%s.%s", protected64, payload64)))
verified := JOSEVerify(pub, hash[:], sig)
inspected := &InspectableToken{
Public: pub,
Protected: protected,
Payload: data,
Signature: signature64,
Verified: verified,
Errors: errs,
}
return inspected, nil
}
type OTP struct {
//Attempts int `json:"attempts"`
CreatedAt time.Time `json:"created_at"`