-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Add auth support to [Reddit] badges #10790
base: master
Are you sure you want to change the base?
Conversation
|
|
||
// Abstract class for Reddit badges | ||
// Authorization flow based on https://github.com/reddit-archive/reddit/wiki/OAuth2#application-only-oauth. | ||
export default class RedditBase extends BaseJsonService { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is partly based on https://github.com/badges/shields/blob/master/services/twitch/twitch-base.js. However:
- there are some annoying differences compared to Twitch's OAuth flow.
- the logic in the Twitch code is partly broken. For example, the loop here does not refresh the request object, so we're querying new access tokens but still using the old one in our retries, which defeats the whole purpose of the retry loop. Also, I noticed whilst experimenting that err.response could be undefined in some cases, making the conditional blow up.
I decided to just try to make things work for Reddit as a fist iteration, and will came back to rework things at a later point.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah agreed. Last time I looked at the code for twitch I thought there are some issues that want fixing. Lets not copy the approach.
@@ -24,6 +24,7 @@ Production hosting is managed by the Shields ops team: | |||
| Cloudflare (CDN) | Access management | @espadrine | | |||
| Cloudflare (CDN) | Admin access | @calebcartwright, @chris48s, @espadrine, @paulmelnikow, @PyvesB | | |||
| Twitch | OAuth app | @PyvesB | | |||
| Reddit | OAuth app | @chris48s, @PyvesB | |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I reused the account that had been created alongside #9817 (comment) and shared the login details in Keybase.
The credentials are not being picked up as this is from a fork, so we're getting the existing state from the master branch, i.e. tests failing. They pass locally when credentials are configured, I guess we can simply merge this once the review is completed, and see what happens. |
Couple of questions on the token:
|
@@ -29,8 +28,6 @@ export default class RedditSubredditSubscribers extends BaseJsonService { | |||
}, | |||
} | |||
|
|||
static _cacheLength = 7200 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Related to the question about what rate limit we get with authentication, are we confident that we'd have enough rate limit to get away with completely ditching this?
const tokenSchema = Joi.object({ | ||
access_token: Joi.string().required(), | ||
expires_in: Joi.number(), | ||
scope: Joi.string(), | ||
token_type: Joi.string(), | ||
}) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The expires_in
value is used. The scope
and token_type
values are discarded. Suggest
const tokenSchema = Joi.object({
access_token: Joi.string().required(),
expires_in: Joi.number().required(),
}).required()
here.
const timeout = setTimeout(() => { | ||
RedditBase._redditToken = this._getNewToken() | ||
}, replaceTokenMs) | ||
|
||
// do not block program exit | ||
timeout.unref() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This approach using setTimeout()
isn't wrong, and I'm not saying we have to change it.
That said, just as a data point: We implemented something quite similar to this in the JWT auth helper.
shields/core/base-service/auth-helper.js
Lines 256 to 318 in 41d072e
static _isJwtValid(expiry) { | |
// we consider the token valid if the expiry | |
// datetime is later than (now + 1 minute) | |
return dayjs.unix(expiry).isAfter(dayjs().add(1, 'minutes')) | |
} | |
async _getJwt(loginEndpoint) { | |
const { _user: username, _pass: password } = this | |
// attempt to get JWT from cache | |
if ( | |
jwtCache?.[loginEndpoint]?.[username]?.token && | |
jwtCache?.[loginEndpoint]?.[username]?.expiry && | |
this.constructor._isJwtValid(jwtCache[loginEndpoint][username].expiry) | |
) { | |
// cache hit | |
return jwtCache[loginEndpoint][username].token | |
} | |
// cache miss - request a new JWT | |
const originViolation = !this.isAllowedOrigin(loginEndpoint) | |
if (originViolation) { | |
throw new InvalidParameter({ | |
prettyMessage: 'requested origin not authorized', | |
}) | |
} | |
const { buffer } = await checkErrorResponse({})( | |
await fetch(loginEndpoint, { | |
method: 'POST', | |
form: { username, password }, | |
}), | |
) | |
const json = validate( | |
{ | |
ErrorClass: InvalidResponse, | |
prettyErrorMessage: 'invalid response data from auth endpoint', | |
}, | |
parseJson(buffer), | |
Joi.object({ token: Joi.string().required() }).required(), | |
) | |
const token = json.token | |
const expiry = this.constructor._getJwtExpiry(token) | |
// store in the cache | |
if (!(loginEndpoint in jwtCache)) { | |
jwtCache[loginEndpoint] = {} | |
} | |
jwtCache[loginEndpoint][username] = { token, expiry } | |
return token | |
} | |
async _getJwtAuthHeader(loginEndpoint) { | |
if (!this.isConfigured) { | |
return undefined | |
} | |
const token = await this._getJwt(loginEndpoint) | |
return { Authorization: `Bearer ${token}` } | |
} |
One thing that is quite nice about that approach is that the control flow is a bit more linear.
Dunno if you reckon that is any better/worse? If you just want to roll with this then I think it is ok.
Fixes #9256.