1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
|
import flask
import urllib.parse
from flask import request
# Application Config
OIDC_ISSUER = "https://id.crute.me/"
OIDC_AUTH_ENDPOINT = "{}login".format(OIDC_ISSUER)
OIDC_JWKS_ENDPOINT = "{}jwks".format(OIDC_ISSUER)
DOMAIN_WHITELIST = ["crute.me", "crute.us"]
# Generic Constants
JRD_MIMETYPE = "application/jrd+json"
OIDC_ISSUER_QUERY = "http://openid.net/specs/connect/1.0/issuer"
JRD_404 = flask.Response("Not found", status=404, mimetype=JRD_MIMETYPE)
app = flask.Flask(__name__)
def parse_email_addr(addr):
try:
user, domain = addr.split("@")
return user, domain
except ValueError:
return None
@app.route("/.well-known/webfinger", methods=["GET"])
def webfinger():
resource = request.args.get("resource")
rel = request.args.get("rel")
# Only support OIDC queries
if not rel or not resource or not rel == OIDC_ISSUER_QUERY:
return JRD_404
# Only support email address queries
acct = urllib.parse.urlparse(resource)
if acct.scheme != "acct":
return JRD_404
# Ensure that the query is in a whitelisted domain
_, domain = parse_email_addr(acct.path)
if domain not in DOMAIN_WHITELIST:
return JRD_404
# We don't validate that the user exists to prevent leaking information
# about what users exist; plus, it doesn't really matter since the querier
# only cares about the OIDC issuer endpoint which is user independent
res = flask.jsonify({
"subject": resource,
"links": [{
"rel": OIDC_ISSUER_QUERY,
"href": OIDC_ISSUER,
}]
})
res.mimetype = JRD_MIMETYPE
return res
@app.route("/.well-known/openid-configuration", methods=["GET"])
def openid_configuration():
return flask.jsonify({
"issuer": OIDC_ISSUER,
"authorization_endpoint": OIDC_AUTH_ENDPOINT,
"jwks_uri": OIDC_JWKS_ENDPOINT,
"scopes_supported": ["openid"],
"response_types_supported": ["id_token"],
"response_modes_supported": ["query"],
"grant_types_supported": ["implicit"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["RS256"],
"acr_values_supported": ["want_mfa", "need_mfa"],
})
@app.route("/login", methods=["GET", "POST"])
def login():
pass
@app.route("/jwks", methods=["GET"])
def keys():
pass
|