staticman
staticman copied to clipboard
About OAuth support
The OAuth support in Staticman is buggy and very limited. Although is had been there years, I can hardly find any site using this feature.
Currently the OAuth feature:
- is only used to retrieve the user properties such as name or email. (see
Staticman.js::_checkAuth
andStaticman.js::_applyGeneratedFields
) - has nothing to do with how Staticman access your repository.
- is limited to the same git service. You can't authenticate with GitHub on site hosted on GitLab.
- doesn't handle the redirection. It's your responsibility redirecting guest to OAuth provider.
- failed when profile has no email available. (on GitHub you can leave it empty) (No related issue yet)
Can someone explain why and how is this feature added at then? What is the plan in future?
Unfortunately at the moment there's not much capacity in the dev team to provide this kind of support.
Personally I'd like to dig into the matter of Oauth to improve the UX (i.e. removing the need for a GH account dedicated to staticman, etc), I'll get back here when I'll be able to do so.
@pingu8007 it sounds like you have ideas on how to improve the situation though, can you maybe propose some solution for the various points?
Personally I will remove them in future, since I don't like hook too many scripts on my static website :p
Another option is rebrand as user profile provider. Here is what I thought:
External dependencies used by Staticman can be classify into user profile
, spam filter
, git service
and notification service
. For user profile, I guess standardizing the procedure how we retrieve user profile should be a good start:
- User invoke a route that redirect to OAuth provider's
auth
endpoint. This is new feature.- route contains name of OAuth profile defined in siteConfig. Multiple profiles can be defined.
- there can be some mechanisms fallback to global profile if site owner don't want to maintain a personal OAuth App.
- Staticman derive 302 redirect from current path parameters and specified OAuth profile.
-
scope
andclient_id
are read from profile. -
redirect_url
should beauth
endpoint of Staticman generated from current path parameters. -
state
can be used to store sessional data, or salted-hash of them. - current OAuth profile should also be included (by path parameter or
state
). This is what currently missing from Staticman.
-
- generated
redirect_url
can prevent wrong profile from been used. (usually caused by typo)
- Once authorized,
code
is 302 redirected toredirect_url
, i.e.auth
endpoint of Staticman.- Currently Staticman will exchange for access_token, encrypt them and pass back.
- Some provider support OpenID connect, which we can get user profile as well as access_token in single request.
- I would prefer gathering all profile we need in this step so that we don't need to care where the access_token used for.
- We can encrypt profile and put into some field that will be submitted later.
- We can warn user there profile doesn't comply with
requireFields
. - Gathering user profile here should be able to reduce execution time when comment submitted. It would be helpful if the provider locate in somewhere bad on latency.
- I'm not clear how can I collect those response without losing filled form, yet.
- When new comment arrived, Staticman exchange for user profile through
Staticman.js::_checkAuth
.- Staticman need to know where the access_token used for, which is stored in
options['auth-type']
. - If user profile are collected already, then only Spam Filter may cause execution timeout before file committed. Yay.
- Staticman need to know where the access_token used for, which is stored in
- Staticman apply spam check, generated fields, transformation and commit to git service.
With something likes this in siteConfig, which provider should be supported can be fully hand over to site owner.
{
"idp": {
"google": { // oauth app provided by instance
"extend": "staticman-auth-google"
},
"gitlab-com": { // well-known provider with custom client id
"extend": "staticman-auth-gitlab",
"client_id": "some_client_id",
"client_secret": "some_encrypted_client_secret"
},
"gitlab-on-premises": { // private instance of well-known provider
"extend": "staticman-auth-gitlab",
"client_id": "other_client_id",
"client_secret": "other_encrypted_client_secret",
"auth_url": "https://example.org/oauth/auth",
"token_url": "https://example.org/oauth/token"
},
"third-party-provider": { // provider without built-in supported
"client_id": "another_client_id",
"client_secret": "another_encrypted_client_secret",
"auth_url": "https://example.com/oauth/auth",
"token_url": "https://example.com/oauth/token",
"scope": "openid profile email",
"mapping": {
"email": "custom_field_email",
"name": "display_name",
"username": "login",
"url": "homepage"
}
}
}
}
I did some investigations about OpenId Connect (OIDC) and got something confirmed:
- OIDC has its discovery mechanism. All we need is
/.well-known/openid-configuration
. - Only OIDC client (e.g. openid-client) and few line of configurations required. (and OAuth clientId/secret, of course)
- Profile can be wrapped inside
id_token
and/or available fromuserinfo
endpoint. - Providers that works with OIDC: GitLab, Google, Microsoft, Yahoo.com, Yahoo.co.jp, SalesForce, Apple, LINE
- Providers that works with OAuth2: Facebook, GitHub
- Providers that require special logic: Twitter, WordPress
- User can setup personal authorization server with something like ownCloud or Azure AD, if they can't or don't want to use public service.
Here is an example from GitLab's userinfo
endpoint with scope openid+profile+email
:
{
"website": "MY_WEBSITE",
"profile": "MY_GITLAB_PROFILE",
"sub": "INTERNAL_ID",
"picture": "MY_AVATAR_LOCATION",
"email_verified": true,
"groups": [
"MY_GROUP_1",
"MY_GROUP_2"
],
"sub_legacy": "INTERNAL_ID",
"nickname": "pingu8007",
"email": "MY_EMAIL_ADDRESS",
"name": "Pin Gu"
}
And it's an example from Google, which wrapped profile inside id_token
:
{
"iss": "https://accounts.google.com",
"azp": "407408718192.apps.googleusercontent.com",
"aud": "407408718192.apps.googleusercontent.com",
"sub": "INTERNAL_GOOGLE_USER_ID",
"email": "MY_EMAIL_ADDRESS",
"email_verified": true,
"at_hash": "HASH_OF_ACCESS_TOKEN",
"name": "Pin Gu",
"picture": "MY_AVATAR_HOSTED_ON_GOOGLE",
"given_name": "Pin",
"family_name": "Gu",
"locale": "zh-TW",
"iat": 1597717719,
"exp": 1597721319
}
This isn't something we can pick up right away, but it's definitely something to add to the backlog. I agree with your assessment that the git provider and the profile provider don't need to be coupled.
I think in the end we could simplify this whole flow to one Staticman API call which is submitted by the site's form. Redirects would handle the rest. We could move the auth info to somewhere optional in the entry
request so one call contains all the info to auth and post content. This will require further thought to handle the following cases:
- A user submits two comments on the same blog. They should only have to log in once
- Maybe here is a chance to use that encrypted
access_token
from the oauth request
- Maybe here is a chance to use that encrypted
- A user logs in without submitting anything.
- This may be justification for keeping the separate auth endpoint
- Supporting this isn't necessarily the primary goal of staticman, but it would be cool to support
Thanks for all the initial work on this. OIDC looks like a pretty good fit.
Thank you.