add endpoint to get emails
This commit is contained in:
parent
f13dc593b0
commit
881bf97334
238
mockid/route.go
238
mockid/route.go
|
@ -283,7 +283,7 @@ func Route(jwksPrefix string, privkey keypairs.PrivateKey) http.Handler {
|
||||||
xkeypairs.Object{},
|
xkeypairs.Object{},
|
||||||
xkeypairs.Object{
|
xkeypairs.Object{
|
||||||
"sub": sub,
|
"sub": sub,
|
||||||
"iss": baseURL,
|
"iss": baseURL + "/",
|
||||||
"jti": base64.RawURLEncoding.EncodeToString(nonce),
|
"jti": base64.RawURLEncoding.EncodeToString(nonce),
|
||||||
"iat": time.Now().Unix(),
|
"iat": time.Now().Unix(),
|
||||||
"exp": "1h",
|
"exp": "1h",
|
||||||
|
@ -302,6 +302,84 @@ func Route(jwksPrefix string, privkey keypairs.PrivateKey) http.Handler {
|
||||||
w.Write(b)
|
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) {
|
http.HandleFunc("/api/new-nonce", requireHashcash(func(w http.ResponseWriter, r *http.Request) {
|
||||||
indexURL := getBaseURL(r) + "/api/directory"
|
indexURL := getBaseURL(r) + "/api/directory"
|
||||||
w.Header().Set("Link", "<"+indexURL+">;rel=\"index\"")
|
w.Header().Set("Link", "<"+indexURL+">;rel=\"index\"")
|
||||||
|
@ -501,93 +579,39 @@ func Route(jwksPrefix string, privkey keypairs.PrivateKey) http.Handler {
|
||||||
if "" == token {
|
if "" == token {
|
||||||
token = r.URL.Query().Get("access_token")
|
token = r.URL.Query().Get("access_token")
|
||||||
if "" == 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
|
return
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
parts := strings.Split(token, " ")
|
parts := strings.Split(token, " ")
|
||||||
if 2 != len(parts) {
|
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
|
return
|
||||||
}
|
}
|
||||||
token = parts[1]
|
token = parts[1]
|
||||||
}
|
}
|
||||||
|
|
||||||
parts := strings.Split(token, ".")
|
inspected, err := verifyToken(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)
|
|
||||||
if nil != err {
|
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
|
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, "", " ")
|
tokenB, _ := json.MarshalIndent(inspected, "", " ")
|
||||||
if nil != err {
|
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
|
return
|
||||||
}
|
}
|
||||||
fmt.Fprintf(w, string(tokenB)+"\n")
|
fmt.Fprintf(w, string(tokenB)+"\n")
|
||||||
|
@ -680,6 +704,76 @@ func Route(jwksPrefix string, privkey keypairs.PrivateKey) http.Handler {
|
||||||
return http.DefaultServeMux
|
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 {
|
type OTP struct {
|
||||||
//Attempts int `json:"attempts"`
|
//Attempts int `json:"attempts"`
|
||||||
CreatedAt time.Time `json:"created_at"`
|
CreatedAt time.Time `json:"created_at"`
|
||||||
|
|
Loading…
Reference in New Issue