add easy header and exp options
This commit is contained in:
		
							parent
							
								
									a33b0dbf6a
								
							
						
					
					
						commit
						10450e9b98
					
				
							
								
								
									
										82
									
								
								mockid.go
									
									
									
									
									
								
							
							
						
						
									
										82
									
								
								mockid.go
									
									
									
									
									
								
							@ -12,6 +12,7 @@ import (
 | 
				
			|||||||
	"log"
 | 
						"log"
 | 
				
			||||||
	"math/big"
 | 
						"math/big"
 | 
				
			||||||
	"net/http"
 | 
						"net/http"
 | 
				
			||||||
 | 
						"net/url"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
@ -79,9 +80,37 @@ func main() {
 | 
				
			|||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			scheme = "http://"
 | 
								scheme = "http://"
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		_, _, token := genToken(scheme + r.Host, priv)
 | 
							_, _, token := genToken(scheme+r.Host, priv, r.URL.Query())
 | 
				
			||||||
		fmt.Fprintf(w, token)
 | 
							fmt.Fprintf(w, token)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
						http.HandleFunc("/authorization_header", func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
 | 
							log.Printf("%s %s\n", r.Method, r.URL.Path)
 | 
				
			||||||
 | 
							var scheme string
 | 
				
			||||||
 | 
							if nil != r.TLS || "https" == r.Header.Get("X-Forwarded-Proto") {
 | 
				
			||||||
 | 
								scheme = "https://"
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								scheme = "http://"
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var header string
 | 
				
			||||||
 | 
							headers, _ := r.URL.Query()["header"]
 | 
				
			||||||
 | 
							if 0 == len(headers) {
 | 
				
			||||||
 | 
								header = "Authorization"
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								header = headers[0]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							var prefix string
 | 
				
			||||||
 | 
							prefixes, _ := r.URL.Query()["prefix"]
 | 
				
			||||||
 | 
							if 0 == len(prefixes) {
 | 
				
			||||||
 | 
								prefix = "Bearer "
 | 
				
			||||||
 | 
							} else {
 | 
				
			||||||
 | 
								prefix = prefixes[0]
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							_, _, token := genToken(scheme+r.Host, priv, r.URL.Query())
 | 
				
			||||||
 | 
							fmt.Fprintf(w, "%s: %s%s", header, prefix, token)
 | 
				
			||||||
 | 
						})
 | 
				
			||||||
	http.HandleFunc("/key.jwk.json", func(w http.ResponseWriter, r *http.Request) {
 | 
						http.HandleFunc("/key.jwk.json", func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
		log.Printf("%s %s", r.Method, r.URL.Path)
 | 
							log.Printf("%s %s", r.Method, r.URL.Path)
 | 
				
			||||||
		fmt.Fprintf(w, `{ "kty": "EC" , "crv": %q , "d": %q , "x": %q , "y": %q , "ext": true , "key_ops": ["sign"] }`, jwk.Crv, jwk.D, jwk.X, jwk.Y)
 | 
							fmt.Fprintf(w, `{ "kty": "EC" , "crv": %q , "d": %q , "x": %q , "y": %q , "ext": true , "key_ops": ["sign"] }`, jwk.Crv, jwk.D, jwk.X, jwk.Y)
 | 
				
			||||||
@ -108,12 +137,12 @@ func main() {
 | 
				
			|||||||
	})
 | 
						})
 | 
				
			||||||
	fs := http.FileServer(http.Dir("public"))
 | 
						fs := http.FileServer(http.Dir("public"))
 | 
				
			||||||
	http.Handle("/", fs)
 | 
						http.Handle("/", fs)
 | 
				
			||||||
/*
 | 
						/*
 | 
				
			||||||
		http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
 | 
							http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
 | 
				
			||||||
			log.Printf(r.Method, r.URL.Path)
 | 
								log.Printf(r.Method, r.URL.Path)
 | 
				
			||||||
			http.Error(w, "Not Found", http.StatusNotFound)
 | 
								http.Error(w, "Not Found", http.StatusNotFound)
 | 
				
			||||||
		})
 | 
							})
 | 
				
			||||||
*/
 | 
						*/
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fmt.Printf("Serving on port %d\n", port)
 | 
						fmt.Printf("Serving on port %d\n", port)
 | 
				
			||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
@ -125,7 +154,7 @@ func main() {
 | 
				
			|||||||
	fmt.Printf("Private Key:\n\t%s\n", string(b))
 | 
						fmt.Printf("Private Key:\n\t%s\n", string(b))
 | 
				
			||||||
	b, _ = json.Marshal(jwk.PublicJWK)
 | 
						b, _ = json.Marshal(jwk.PublicJWK)
 | 
				
			||||||
	fmt.Printf("Public Key:\n\t%s\n", string(b))
 | 
						fmt.Printf("Public Key:\n\t%s\n", string(b))
 | 
				
			||||||
  protected, payload, token := genToken(host, priv)
 | 
						protected, payload, token := genToken(host, priv, url.Values{})
 | 
				
			||||||
	fmt.Printf("Protected (Header):\n\t%s\n", protected)
 | 
						fmt.Printf("Protected (Header):\n\t%s\n", protected)
 | 
				
			||||||
	fmt.Printf("Payload (Claims):\n\t%s\n", payload)
 | 
						fmt.Printf("Payload (Claims):\n\t%s\n", payload)
 | 
				
			||||||
	fmt.Printf("Access Token:\n\t%s\n", token)
 | 
						fmt.Printf("Access Token:\n\t%s\n", token)
 | 
				
			||||||
@ -133,15 +162,56 @@ func main() {
 | 
				
			|||||||
	<-done
 | 
						<-done
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func genToken(host string, priv *ecdsa.PrivateKey) (string, string, string) {
 | 
					func parseExp(exp string) (int, error) {
 | 
				
			||||||
 | 
						if "" == exp {
 | 
				
			||||||
 | 
							exp = "15m"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						mult := 1
 | 
				
			||||||
 | 
						switch exp[len(exp)-1] {
 | 
				
			||||||
 | 
						case 'w':
 | 
				
			||||||
 | 
							mult *= 7
 | 
				
			||||||
 | 
							fallthrough
 | 
				
			||||||
 | 
						case 'd':
 | 
				
			||||||
 | 
							mult *= 24
 | 
				
			||||||
 | 
							fallthrough
 | 
				
			||||||
 | 
						case 'h':
 | 
				
			||||||
 | 
							mult *= 60
 | 
				
			||||||
 | 
							fallthrough
 | 
				
			||||||
 | 
						case 'm':
 | 
				
			||||||
 | 
							mult *= 60
 | 
				
			||||||
 | 
							fallthrough
 | 
				
			||||||
 | 
						case 's':
 | 
				
			||||||
 | 
							// no fallthrough
 | 
				
			||||||
 | 
						default:
 | 
				
			||||||
 | 
							// could be 'k' or 'z', but we assume its empty
 | 
				
			||||||
 | 
							exp += "s"
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						num, err := strconv.Atoi(exp[:len(exp)-1])
 | 
				
			||||||
 | 
						if nil != err {
 | 
				
			||||||
 | 
							return 0, err
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
						return num * mult, nil
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func genToken(host string, priv *ecdsa.PrivateKey, query url.Values) (string, string, string) {
 | 
				
			||||||
	thumbprint := thumbprintKey(&priv.PublicKey)
 | 
						thumbprint := thumbprintKey(&priv.PublicKey)
 | 
				
			||||||
	protected := fmt.Sprintf(`{"typ":"JWT","alg":"ES256","kid":"%s"}`, thumbprint)
 | 
						protected := fmt.Sprintf(`{"typ":"JWT","alg":"ES256","kid":"%s"}`, thumbprint)
 | 
				
			||||||
	protected64 := base64.RawURLEncoding.EncodeToString([]byte(protected))
 | 
						protected64 := base64.RawURLEncoding.EncodeToString([]byte(protected))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						exp, err := parseExp(query.Get("exp"))
 | 
				
			||||||
 | 
						if nil != err {
 | 
				
			||||||
 | 
							// cryptic error code
 | 
				
			||||||
 | 
							// TODO propagate error
 | 
				
			||||||
 | 
							exp = 422
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	payload := fmt.Sprintf(
 | 
						payload := fmt.Sprintf(
 | 
				
			||||||
		`{"iss":"%s/","sub":"dummy","exp":%s}`,
 | 
							`{"iss":"%s/","sub":"dummy","exp":%s}`,
 | 
				
			||||||
		host, strconv.FormatInt(time.Now().Add(15*time.Minute).Unix(), 10),
 | 
							host, strconv.FormatInt(time.Now().Add(time.Duration(exp)*time.Second).Unix(), 10),
 | 
				
			||||||
	)
 | 
						)
 | 
				
			||||||
	payload64 := base64.RawURLEncoding.EncodeToString([]byte(payload))
 | 
						payload64 := base64.RawURLEncoding.EncodeToString([]byte(payload))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	hash := sha256.Sum256([]byte(fmt.Sprintf(`%s.%s`, protected64, payload64)))
 | 
						hash := sha256.Sum256([]byte(fmt.Sprintf(`%s.%s`, protected64, payload64)))
 | 
				
			||||||
	r, s, _ := ecdsa.Sign(rand.Reader, priv, hash[:])
 | 
						r, s, _ := ecdsa.Sign(rand.Reader, priv, hash[:])
 | 
				
			||||||
	rb := r.Bytes()
 | 
						rb := r.Bytes()
 | 
				
			||||||
 | 
				
			|||||||
@ -4,14 +4,15 @@ Compatible with
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
* OAuth2
 | 
					* OAuth2
 | 
				
			||||||
* OpenID Connect (OIDC)
 | 
					* OpenID Connect (OIDC)
 | 
				
			||||||
* JOSE
 | 
					* JOSE (Signed Access Tokens)
 | 
				
			||||||
  * JWT
 | 
					  * JWT
 | 
				
			||||||
  * JWK (Signed Access Tokens)
 | 
					  * JWK
 | 
				
			||||||
  * JWS
 | 
					  * JWS
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<h1>Resources</h1>
 | 
					<h1>Resources</h1>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  * https://mock.pocketid.app/access_token
 | 
					  * https://mock.pocketid.app/access_token
 | 
				
			||||||
 | 
					  * https://mock.pocketid.app/authorization_header
 | 
				
			||||||
  * https://xxx.mock.pocketid.app/.well-known/openid-configuration
 | 
					  * https://xxx.mock.pocketid.app/.well-known/openid-configuration
 | 
				
			||||||
  * https://xxx.mock.pocketid.app/.well-known/jwks.json
 | 
					  * https://xxx.mock.pocketid.app/.well-known/jwks.json
 | 
				
			||||||
  * https://mock.pocketid.app/key.jwk.json
 | 
					  * https://mock.pocketid.app/key.jwk.json
 | 
				
			||||||
@ -26,6 +27,22 @@ For example:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  TOKEN=$(curl -fL https://mock.pocketid.app/access_token)
 | 
					  TOKEN=$(curl -fL https://mock.pocketid.app/access_token)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					You can set the expiration (`exp`) with query parameters:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  * https://mock.pocketid.app/access_token?exp=90d (+90 days)
 | 
				
			||||||
 | 
					  * https://mock.pocketid.app/access_token?exp=3h (+3 hours)
 | 
				
			||||||
 | 
					  * https://mock.pocketid.app/access_token?exp=15m (+15 minutes)
 | 
				
			||||||
 | 
					  * https://mock.pocketid.app/access_token?exp=1565739000 (exactly 2019-08-13 5:30pm)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					(you can also get the whole header)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  https://mock.pocketid.app/authorization_header
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					For example:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  HEADER=$(curl -fL https://mock.pocketid.app/authorization_header)
 | 
				
			||||||
 | 
					  # Authorization: Bearer <token>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<h3>The Token, Decoded</h3>
 | 
					<h3>The Token, Decoded</h3>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The Token will look like this:
 | 
					The Token will look like this:
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user