diff --git a/ext/oauth-client/ang/oauthJwtDebug.aff.html b/ext/oauth-client/ang/oauthJwtDebug.aff.html index bafa5dd7257d..5096a2489182 100644 --- a/ext/oauth-client/ang/oauthJwtDebug.aff.html +++ b/ext/oauth-client/ang/oauthJwtDebug.aff.html @@ -5,29 +5,27 @@

Some (but not all) OAuth2 tokens are based on JWT. If a token is based on JWT, then we can examine its content to learn more about what the token signifies. This may help with debugging token-access issues.

-

Token ID

+
- - -
- {{ts('Inspect')}} +
+ No tokens found.
+
+ +

Token Record

-
+
{{token|json}}

Access Token: Raw

-
{{data.token[0].access_token}}
+
{{token.access_token}}

Access Token: JWT Payload

-
{{data.token[0].access_token|unvalidatedJwtDecode|json}}
+ (This will only display meaningful data if the token is based on JWT.) + +
{{token.access_token|unvalidatedJwtDecode|json}}
diff --git a/ext/oauth-client/backlog.md b/ext/oauth-client/backlog.md new file mode 100644 index 000000000000..f9f2b31e196a --- /dev/null +++ b/ext/oauth-client/backlog.md @@ -0,0 +1,19 @@ +(Things that are not done - though they may be started/WIP) + +# More urgent + +* File bug details about MSEO + +# Middle urgent + + +# Less urgent + +* E2E test coverage for grants +* OAuth2 Dev docs + * How to register demo site +* `hook_mailSetupActions` docs +* `hook_civicrm_alterMailStore` ([examples](https://github.com/civicrm/civicrm-core/pull/18902#)) +* OAuthUserToken +* IMAP GUI: Hide password field if there's an oauth2 token +* OAuth2 GUI: Add token via UserPassword and ClientCredential diff --git a/ext/oauth-client/draft-docs.md b/ext/oauth-client/draft-docs.md new file mode 100644 index 000000000000..35d58e7afaf3 --- /dev/null +++ b/ext/oauth-client/draft-docs.md @@ -0,0 +1,268 @@ +# OAuth2 Client Administration (Generic Guide) + +OAuth2 defines a protocol for connecting to web-services. With the `oauth-client` extension, CiviCRM can act as a +client that reads and writes data on remote OAuth2 services (such as Google, Microsoft, Twitter, or Facebook). + +Ideally, documentation might be tuned to the the particular service. But we don't have that right now, so +let's consider the general formula. + +## Enable and identify the CiviCRM "Redirect URL" + +???+ howto "Enable and identify via web UI" + + * Login to CiviCRM and enable the `oauth-client` extension + * Navigate to "Administer => System Settings => OAuth2" (`civicrm/admin/oauth`) + * At the bottom of this page, it will report a "Redirect URL" (e.g. `https://example.com/civicrm/oauth-client/return`). + + Make a note of the final redirect URL - you will need it later. + +??? howto "Enable and identify via CLI" + + ``` + $ cv en oauth_client + Enabling extension "oauth_client" + + $ cv url 'civicrm/oauth-client/return' + "https://example.com/civicrm/oauth-client/return" + ``` + + Make a note of the final redirect URL - you will need it later. + +??? question "What happens if my Redirect URL is private/local/internal?" + + The "Redirect URL" is primarily used by your local web-browser. Consequently, it does not need to be available on the public Internet. + + However, the remote web-service may have restrictions -- OAuth2 services generally requires secure "HTTPS" URL. For local development + on insecure "HTTP" URLs, it only supports `http://localhost:NNN` and `http://127.0.0.1:NNN`. If you use a different hostname or IP + address, then you will need a workaround, e.g. + + * Change the local development environment to support HTTPS or to use the literal URL `http://localhost` or `http://127.0.0.1`. + * Run the bundled script `bin/local-redir.sh`. This runs a dummy HTTP service `http://127.0.0.1:3000` which you can use as the "Redirect URL". + Let it run while doing development. + * Find a public HTTPS server. Add a redirect rule or stub script which will redirect to your preferred URL + (`https://PUBLIC.EXAMPLE.COM/FIXME` => `http://PRIVATE.EXAMPLE.COM/civicrm/oauth-client/return`). Inform CiviCRM of this URL: + + ``` + $ cv api setting.create oauthClientRedirectUrl='https://PUBLIC.EXAMPLE.COM/FIXME' + ``` + + You may be able to use public services like https://tinyurl.com, but this will depend on the specific restrictions / policies / payloads involved. + +## Register the client application with your web-service provider + +The registration steps will vary depending on the web-service that CiviCRM is integrating with. +Below, we include some generic instructions. Over time, this may be expanded to include more +specific examples. + +???+ howto "Register a client (generic)" + + * Login to the developer or system administration console for your web-service. + * Locate the section for registering applications. + * Create a new application: + * The application will require a "Client ID" and "Client Secret" (together, these may be called "Client Credentials"). + These are usually generated automatically and consituted by a long, random string. Make note of these values - you will need them later. + * The application will require a "Redirect URL". Copy and past the URL that you identified earlier. + +## Register the client application within CiviCRM + +???+ howto "Register the client via web UI" + + * Login to CiviCRM and enable the `oauth-client` extension + * Navigate to "Administer => System Settings => OAuth2" (`civicrm/admin/oauth`) + * Choose "New Client". + * Select the "Provider" which matches the remote web-service. + * Copy in the "Client ID (GUID)" and "Client Secret" from earlier. + +??? howto "Register the client via CLI" + + First, we need to identify the type of "Provider" that you will connect to. The `OAuthProvider.get` will list available options + + ``` + $ cv api4 OAuthProvider.get -T +s name,title + +-------------+----------------------------+ + | name | title | + +-------------+----------------------------+ + | ms-exchange | Microsoft: Exchange Online | + | demo | Local Demo | + +-------------+----------------------------+ + ``` + + Note the provider's name and recall the "Client ID" and "Client Secret" from earlier. Pass these to `OAuthClient.create`: + + ``` + $ cv api4 OAuthClient.create +v provider="NAME" +v guid="CLIENT_ID" +v secret="CLIENT_SECRET" + ``` + +??? question "What if the provider I want does not appear?" + + This likely indicates that there is no integration. Of course, if you are doing development or experimentation, then you + should see the development section. + +## Grant access + +OAuth2 defines a few ways to grant access. Support for these will: + +* + +# OAuth2 Client Development + + +## Define a provider + +Signficance of class, league/oauth2-client, options, and mailSettingsTemplate + +??? howto "Define a provider via PHP hook" +??? howto "Define a provider via JSON file" + +## Create a client + +```php +$client = civicrm_api4('OAuthClient', 'create', [ + 'provider' => 'NAME', + 'guid' => 'CLIENT_ID', + 'secret' => 'CLIENT_SECRET', +])->single(); +``` + + +??? question "What is the difference between client "ID" and "GUID"?" + + In OAuth2's specification, the client "ID" refers to a long, public identifier (often random alphanumerics) registered with the remote + web-service. In CiviCRM's data management system, an "ID" is an internal, incrementing integer. Each `OAuthClient` will have both + identifiers, stored in two fields: + + * The `id` is the local/internal identifier required by the data-management layer. It is an incrementing integer. + * The `guid` is the public/external identifier. It is the value presented in web-service requests. + + Depending on context, the word "ID" may describe either. But in technical examples referencing the Civi API, the symbols `id` and + `guid` should be interpreted as `id` (local/internal) and `guid` (public/external). + +## Grant access + +The OAuth2 protocol defines several mechanisms for authenticating to a web-service. There are several common elements -- in each case, one must: + +* Identify the client (e.g. using the public ID and the secret). +* Request a set of permissions ("scopes"). +* Perform some kind of authentication "flow". (There are ~3 common variations.). +* Receive and store an access token. + +The `OAuthClient` entity provides separate methods for each authentication flow. The choice of method will depend on your use-case and +the policies of the remote web-service -- some require a specific flow, and some are flexible. + +Once you've determined the appropriate authentication flow, you can use one these examples to run it: + +??? howto "Grant access via client credentials" + + This is the simplest form of authentication - it does not require any extra information. + + ```php + $token = civicrm_api4('OAuthClient', 'clientCredentials', [ + 'where' => [['id', '=', 123]], + 'scopes' => ['first', 'second'], + ])->single(); + ``` + + If successful, the resulting token is stored for usage. + +??? howto "Grant access via username/password" + + This form is also fairly simple: + + ```php + $token = civicrm_api4('OAuthClient', 'userPassword', [ + 'where' => [['id', '=', 123]], + 'scopes' => ['first', 'second'], + 'username' => 'johndoe', + 'password' => 'abcd1234', + ])->single(); + ``` + +??? howto "Grant access via authorization code" + + In the most well-known OAuth2 flow, one directs the user's browser to visit the remote web-service. + They will confirm that they wish to grant permission - and then redirect back to CiviCRM. + + ```php + $start = civicrm_api4('OAuthClient', 'authorizationCode', [ + 'where' => [['id', '=', 123]], + 'scopes' => ['first', 'second'], + 'landingUrl' => '...', + ])->single(); + CRM_Utils_System::redirect($start['url']); + ``` + + Note that this method cannot complete the process. Instead, it returns `$start['url']` -- you + need to redirect the user to this URL. + +??? question "How are tokens stored?" + + +## Working with access tokens + + + +## Working with access tokens: league/oauth2-client + +## Working with access tokens: Guzzle + +## Working with access tokens: HTTP Headers + +## Working with access tokens: Others + + + +```php +$client = civicrm_api4('OAuthClient', 'get', [ + 'where' => [['id', '=', $clientId]], +])->single(); +$provider = \Civi::service('oauth2.league')->createProvider($client); +``` + +## Hooks + +```php +function hook_civicrm_oauthReturn($tokenRecord, &$nextUrl); +function hook_civicrm_oauthReturnError($error, $description = NULL, $uri = NULL); +``` + +# Core Update + +## hook_civicrm_mailSetupActions + +```php +// https://github.com/civicrm/civicrm-core/pull/18885 + +function mymod_civicrm_mailSetupActions($setupActions) { + $setupActions['supermail'] = [ + 'title' => ts('Super Mail'), + 'callback' => '_mymod_setup', + ]; +} + +function _mymod_setup($setupAction) { + return [ + 'url' => '...the-next-setup-page...', + ]; +} +``` + + +## hook_civicrm_alterMailStore + +https://github.com/civicrm/civicrm-core/pull/18902# + +To add a new protocol FIZZBUZZ, you could: + +* Register a value in the OptionGroup mail_protocol. +* Create a driver class (eg CRM_Mailing_MailStore_FizzBuzz extends CRM_Mailing_MailStore) +* Use the hook to activate the class: + +```php +function hook_civicrm_alterMailStore(&$mailSettings) { + if ($mailSettings['protocol'] === 'FIZZBUZZ') { + $mailSettings['factory'] = function ($mailSettings) { + return new CRM_Mailing_MailStore_FizzBuzz(...); + }; + } +} +```