Table of Contents | ||
---|---|---|
|
With the implementation of
Jira Legacy | ||||||
---|---|---|---|---|---|---|
|
Once a client is registered it can perform scoped operations on behalf of the user, at the time of writing we support the openid scope for the Open ID Connect /userInfo service. In the future we will extend the scope (and claims) that would allow access to data that with the user consent.
One of the issues in the OAuth architecture is that while authentication and authorization is decoupled from a client application in a secure manner, establishing trust on the client application is not defined by the specification.
Since that the data that synapse users can access might potentially be sensitive according to the scopes and claims, we need a way to establish trust with a client application by having a process in place to verify that the application will not misuse and/or abuse the system. There is not a standard (automated or manual) procedure for verifying a client in the industry and different companies take different approaches (or choose not to have a verification process).
Google and Facebook have both an automated and manual verification and review process, according to the type of client, scope and claims the client has access to. (See https://developers.google.com/apps-script/guides/client-verification and https://support.google.com/cloud/answer/9110914?hl=en).
Proposed Approach for Synapse
We propose to introduce a (manual) verification step for application clients that are registered in synapse which is similar to the user verification (https://sagebionetworks.jira.com/wiki/spaces/PLFM/pages/82935813/Verified+User). The manual verification will be implemented on top of an automatic validation of ownership of the domain in the redirect uri(s) submitted by the client. We allow registration and usage without verification but the web client should show a banner if a user consenting to an application that has not been verified. The banner should clearly state that the application is not verified and discourage the user to consent.
Allowing the usage of a client which is not verified can be necessary for testing (note that we could think of a sandbox/public environment for clients but for the time being we want to keep the process as simple as possible).
When a client register an application in synapse currently the following required information is supplied:
Name of the client
Redirect URIs + (Optional) Sector Identifier URI (See https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata)
We do not require the response_type as we default to "code" nor the the application_type (which defaults to "web").
Additionally the following optional information can be supplied:
client_uri: URL of the home page of the Client
policy_uri: URL that the Relying Party Client provides to the End-User to read about the how the profile data will be used
tos_uri: URL that the Relying Party Client provides to the End-User to read about the Relying Party's terms of service
The information above can all be useful to establish trust in a client. We propose the following pre-conditions for submitting a verification:
...
The client, policy and tos uris should be filled out
...
The client, policy and tos uris domain should match that of the redirect uris (As suggested in https://openid.net/specs/openid-connect-registration-1_0.html#Impersonation). This should be validated at registration time
...
The redirect uris scheme should be https and the host should not be localhost or loopback (127.0.0.1, ::1)
...
The client should have generated a secret
...
Verification Procedure
As of November 2019 the OAuth Clients needs to be verified in order to be usable. A dedicated OAuth Verification Job in the ops build system has been created to perform verification of a specific client. The following procedure can be used in order to verify a client:
Once a request is received and the client needs to be verified, create a new ticket in JIRA with the client id
Launch the OAuth Verification Job filling out the required parameters (Note: you will need a Jenkins account):
SESSION_TOKEN: A valid session token for an admin user
CLIENT_ID: the id of the client to verify
ETAG: The etag of the client to verify (this is to ensure that the client didn’t change after the client details were read)
VERIFY_STATUS: Leave enabled (deselecting would un-verify a client)
Resolve the related JIRA issue
Initial Implementation
As from the design review meeting held on the 11th of November 2019 the synapse team decided that due to the number of expected use cases a complete verification process is not needed and the initial implementation will be based on white listing on a case by case the oauth clients:
Client will not be usable after creation until verified. If a client is being (e.g. to get an authorization code or access token) used while not verified an error message should include a the email to contact in order to verify the client.
In the Web Client a form to submit a Jira issue to the ACT team to request the client verification will be shown (as part of
)Jira Legacy server System JIRA serverId ba6fb084-9827-3160-8067-8ac7470f78b2 key SWC-4957 A user should be blocked in the web client from using a non verified client (
)Jira Legacy server System JIRA serverId ba6fb084-9827-3160-8067-8ac7470f78b2 key SWC-5043 An administrative API (Only ACT and admins can invoke this endpoint) will be added to verify a client:
Code Block PUT /admin/oauth2/client/{id}/verified?status=<boolean>
The initial proposed design below is still valid and can be taken into account in the future when/if the verification will be extended.
Proposed Approach for Synapse
We propose to introduce a (manual) verification step for application clients that are registered in synapse which is similar to the user verification (https://sagebionetworks.jira.com/wiki/spaces/PLFM/pages/82935813/Verified+User). The manual verification will be implemented on top of an automatic validation of ownership of the domain in the redirect uri(s) submitted by the client. We allow registration and usage without verification but the web client should show a banner if a user consenting to an application that has not been verified. The banner should clearly state that the application is not verified and discourage the user to consent.
Allowing the usage of a client which is not verified can be necessary for testing (note that we could think of a sandbox/public environment for clients but for the time being we want to keep the process as simple as possible).
When a client register an application in synapse currently the following required information is supplied:
Name of the client
Redirect URIs + (Optional) Sector Identifier URI (See https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata)
We do not require the response_type as we default to "code" nor the the application_type (which defaults to "web").
Additionally the following optional information can be supplied:
client_uri: URL of the home page of the Client
policy_uri: URL that the Relying Party Client provides to the End-User to read about the how the profile data will be used
tos_uri: URL that the Relying Party Client provides to the End-User to read about the Relying Party's terms of service
The information above can all be useful to establish trust in a client. We propose the following pre-conditions for submitting a verification:
The client, policy and tos uris should be filled out
The client, policy and tos uris domain should match at least one of the domains in the list of redirect uris (As suggested in https://openid.net/specs/openid-connect-registration-1_0.html#Impersonation). This should be validated at registration time.
The redirect uris scheme should be https and the host should not be localhost or loopback (127.0.0.1, ::1). Note that a client can still be registered with a localhost or loopback for testing, but verification would not be available.
A natural language description of the application must be included in the submission
...
In case of rejection a reason must be included explaining why. A user should be able to update the client and perform another submission after being rejected.
Domain validation
In addition to the manual approval or rejection, we should establish ownership of the domain referred to by the redirect uri (s) in the client as an additional trust layer. Note that while the specification does not enforce the scheme for redirect uris for web application types (See application_type in https://openid.net/specs/openid-connect-registration-1_0.html#ClientMetadata), it allows custom schemes for "native" applications. Since we do not support native applications and the specification does not explicitly forbid it, we can enforce the redirect uri scheme to be https. In this way we can assume that we should always be able to verify the domain ownership.
...
The validation code is verified by the backend asynchronously, a worker will try and verify all the clients that are still pending. We might want to implement multiple retries before automatically rejecting a verification submission because of a failed domain validation (e.g. code mismatch).
If the sector_identifier_uri is provided during the client registration the list of redirect uris might potentially contain multiple domains. In order for a client to be validated all of the URIs needs to pass the domain validation.
Verification Status
A verification submission is associated with a status, one of: SUBMITTED, REJECTED, APPROVED. A verification approval is tied to the domain validation, the verification will include the information about the domain validation status. The verification cannot be approved if the domain is not validated.
If the verification is reject rejected the user should resubmit the verification with any changes indicated in the rejection reason.
...
We should allow to bypass the verification submission (e.g. for internal use or other mean of verification), in this case a submission from the user is not necessary and the client can be verified with a dedicated API call.
API Changes
There are some API changes needed in order to support this workflow, in particular a client is not usable if a secret was not generated and we therefore required the presence of a secret for verification. Our implementation deviates somehow from the specification, in the sense that the secret is not generated at registration time and is not returned in the GET /oauth2/client
response. Instead we have a dedicated a submission from the user is not necessary and the client can be verified with a dedicated API call.
Proposed API
DT = Designated team for the verification review.
METHOD | URI | Notification | Request | Response | Description |
---|---|---|---|---|---|
POST | /oauth2/client/{id}/verification | To the DT to review the verification request | OAuthClientVerification | OAuthClientVerification | Allows the user to submit a new verification for the client. Only the creator of the client can make this call. |
GET | /oauth2/client/{id}/verification | OAuthClientVerification | Gets the current verification for the client if any. Only the creator of the client can make this call. | ||
GET | /oauth2/client/{id}/verification/validationCode | OAuthClientValidationCode | Allows the user to retrieve the validation code used for domain validation. The code is generated once when the verification is submitted. | ||
POST | POST /oauth2/client/ |
...
{id}/verification/status | To the verification creator | OAuthClientVerificationStatus | OAuthClientVerificationStatus | Allows the DT to approve/reject the verification. The possible state transitions are: SUBMITTED → APPROVED SUBMITTED → REJECTED |
PUT | /admin/oauth2/client/{id}/ |
...
For the web client to show the requirements for verification it needs to be able to gather the current status of the secret, we propose to include a boolean property secret_generated
in the response of the GET /oauth/client/{id}
which would also allow the web client to inform the user that a secret needs to be generated.
Additionally we might want to add a new API call that returns the current secret for the user
GET /oauth2/client/secret/{id}
Proposed API
DT = Designated team for the verification review.
...
METHOD
...
URI
...
Notification
...
Request
...
Response
...
Description
...
POST
...
/oauth2/client/{id}/verification
...
To the DT to review the verification request
...
OAuthClientVerification
...
OAuthClientVerification
...
Allows the user to submit a new verification for the client. Only the creator of the client can make this call.
...
GET
...
/oauth2/client/{id}/verification
...
OAuthClientVerification
...
Gets the current verification for the client if any. Only the creator of the client can make this call.
...
POST
...
POST /oauth2/client/{id}/verification/status
...
To the verification creator
...
OAuthClientVerificationStatus
...
OAuthClientVerificationStatus
...
Allows the DT to approve/reject the verification. The possible state transitions are:
SUBMITTED → APPROVED
SUBMITTED → REJECTED
...
POST
...
/oauth2/client/{id}/verified?status=<boolean>
...
Allows the DT to bypass the verification and set the client as verified or not independently from the verification.
...
GET
...
/oauth2/client/verification
...
OAuthClientVerificationList
...
Allows the DT to retrieve a paginated list of verifications (sorted by creation date desc), optional parameters:
status: Filter by a specific status, defaults to SUBMITTED
createdBy: Filter by the a specific creator
clientId: Filter by the given client id.
nextPageToken: Token received in the previous page, default null
API Models:
OAuthClientVerificationStatus
Field | Type | Description |
---|---|---|
status | string | the status of the verification, one of SUBMITTED, APPROVED, REJECTED |
reason | string | The reason for the rejection |
createdOn | date | The creation date |
createdBy | long | The id of the user that pushed this status. Only present when the caller is the DT |
OAuthClientDomainValidationStatus
Field | Type | Description |
---|---|---|
status | string | the status of the domain validation, one of PENDING, VALIDATED, FAILED |
reason | string | The reason for the validation failure |
createdOn | date | The creation date |
modifiedOn | date | The last modification date |
OAuthClientVerification
Field | Type | Description |
---|---|---|
clientId | long | The id of the client this verification refers to |
clientDescription | string | Required description for the submission |
createdOn | date | The creation date |
createdBy | long | The id of the user that created the verification |
verificationStatus | OAuthClientVerificationStatus | The current verification status |
domainValidationStatus | OAuthClientDomainValidationStatus | The status of the domain validation |
domainValidationCode | string | The code for the domain validation |
OAuthClientVerificationList
Field | Type | Description |
---|---|---|
results | List<OAuthClientVerification> | The page of results |
nextPageToken | string | The token used to retrieve the next page if any. |
...
verified?status=<boolean> | Allows the DT to bypass the verification and set the client as verified or not independently from the verification. | ||||
GET | /oauth2/client/verification | OAuthClientVerificationList | Allows the DT to retrieve a paginated list of verifications (sorted by creation date desc), optional parameters:
|
API Models
OAuthClientVerificationStatus
Field | Type | Description |
---|---|---|
status | string | the status of the verification, one of SUBMITTED, APPROVED, REJECTED |
reason | string | The reason for the rejection |
createdOn | date | The creation date |
createdBy | long | The id of the user that pushed this status. Only present when the caller is the DT |
OAuthClientDomainValidationStatus
Field | Type | Description |
---|---|---|
status | string | the status of the domain validation, one of PENDING, VALIDATED, FAILED |
reason | string | The reason for the validation failure |
createdOn | date | The creation date |
modifiedOn | date | The last modification date |
OAuthClientVerification
Field | Type | Description |
---|---|---|
clientId | long | The id of the client this verification refers to |
clientDescription | string | Required description for the submission |
createdOn | date | The creation date |
createdBy | long | The id of the user that created the verification |
verificationStatus | OAuthClientVerificationStatus | The current verification status |
domainValidationStatus | OAuthClientDomainValidationStatus | The status of the domain validation |
OAuthClientVerificationList
Field | Type | Description |
---|---|---|
results | List<OAuthClientVerification> | The page of results |
nextPageToken | string | The token used to retrieve the next page if any. |
OAuthClientValidationCode
Field | Type | Description |
---|---|---|
cliendId | long | The id of the client |
code | string | The code for the domain validation for any of the registred redirect uris |
User Interaction
There are three main actors involved in the verification
A. The user that registered a client and needs to submit the verification.
B. The user that reviews the submission
C. The end user using the client
For A (tracked in
Jira Legacy | ||||||
---|---|---|---|---|---|---|
|
See that the client is not verified with information about the consequences
Submit a verification if not verified already
Get the domain validation code, with instructions on how to use it. Potentially a file download with the validation code to be placed as is on a web server.
Check the status of the verification, including the domain validation status.
For B the user should be able to:
See the list of submitted verifications
Review the information of the client, including the description submitted with the verification, the client, tos and policy uris and a link to the user profile.
Check the domain validation status for the client, if the domain is not validated yet approval is not allowed.
Approve or reject the verification, allowing to input a reason
For C, the user should be informed if the client is not verified and the consent should be discouraged.
References
https://tools.ietf.org/html/rfc6749
https://tools.ietf.org/html/rfc8252
...