We use web services to store and present benchmarking results, as well as to host the benchmarking coordination itself. To interact with those services the user, or rather the programs invoked by the user, require credentials.
We don't want to store those credentials in the program's repository, because we might want the program itself to be public, apart from other issues with this approach. We also would like to avoid having to ask IT for the credentials. They are busy enough and this would be another non-automated hurdle that slows us down.
Accredit solves this problem by using the user's GitHub authentication to prove their identity and, once proven, provides credentials for various services.
To initiate a request for credentials, the client—the program, acting on the user's behalf—does a POST request to
https://accredit.parseapp.com/requestCredentials
It provides three arguments:
service
: The name of the service for which credentials are requestedkey
: A random key used to identify this requestsecret
: A really, really random secret shared between the client and Accredit
The request will return a text/plain
document containing the URL of
a GitHub OAuth endpoint which the client then opens in the user's
browser. The user authorizes the "Xamarin Accredit" app on GitHub
and is then redirected back to an Accredit URL that requires an
additional confirmation from the user, asking them to make sure that
this request is done on their behalf, and that the key matches the one
generated by the client.
Example:
curl -X POST --data "service=testService&key=doNotUseThisKey&secret=doNotUseThisSecret" \
https://accredit.parseapp.com/requestCredentials
Once the user acknowledges the request in the browser and Accredit
verifies that the user is trusted—because they belong to the xamarin
GitHub organization, for example—an entry in the CredentialsResponse
table on Parse is generated:
curl -X GET \
-H "X-Parse-Application-Id: RAePvLdkN2IHQNZRckrVXzeshpFZTgYif8qu5zuh" \
-H "X-Parse-REST-API-Key: TCq5qEY6Crgr63SHHftQFqEO3PF9fHXdSX65MkLt" \
-G \
https://api.parse.com/1/classes/CredentialsResponse
{
"results": [
{
"createdAt": "2015-07-16T21:39:08.116Z",
"key": "e22fe736-b3d7-4630-8213-ae9a36980fd1",
"objectId": "cqbwUYJTvo",
"success": true,
"updatedAt": "2015-07-16T21:39:08.116Z"
}
]
}
The client queries this table for an entry with key
being the key it
generated, and once the entry is there and success
is true
, it
does a POST request to
https://accredit.parseapp.com/getCredentials
with the arguments
key
: The random keysecret
: The random secret
to finally get the credentials. This call removes the entry from the
CredentialsResponse
table.
Example:
curl -X POST --data "key=doNotUseThisKey&secret=doNotUseThisSecret" https://accredit.parseapp.com/getCredentials
The getCredentials
call will always remove the CredentialsResponse
table entry, so an attacker could just poll the table and immediately
make calls to getCredentials
to deny service to legitimate users.
Another vector is doing repeated calls to requestCredentials
to
create garbage in the internal CredentialsRequest
table and use up
our limit of Parse requests per second.
If a web page managed to embed the OAuth callback and get the user to
click on the Accredit acknowledgment button it would be able to do a
requestCredentials
call first, then go through OAuth, automatically
acknowledge, and then call getCredentials
to acquire credentials
illicitly.
There are a few files in the repository named xxx-TEMPLATE
. Copy
those files, removing the -TEMPLATE
prefix, and insert the required
application keys.