├── .gitignore
├── Locale
└── fr_FR
│ └── translations.php
├── Template
├── user
│ ├── authentication.php
│ ├── create_remote.php
│ └── external.php
├── auth
│ └── login.php
└── config
│ └── integration.php
├── Makefile
├── Schema
├── Mysql.php
├── Postgres.php
└── Sqlite.php
├── .github
├── issue_template.md
└── workflows
│ └── unit_tests.yml
├── Controller
└── OAuthController.php
├── Test
└── PluginTest.php
├── LICENSE
├── Plugin.php
├── README.md
├── Auth
└── GenericOAuth2Provider.php
└── User
└── GenericOAuth2UserProvider.php
/.gitignore:
--------------------------------------------------------------------------------
1 | *.zip
2 |
--------------------------------------------------------------------------------
/Locale/fr_FR/translations.php:
--------------------------------------------------------------------------------
1 | form->label(t('OAuth2 ID'), 'oauth2_user_id') ?>
2 | = $this->form->text('oauth2_user_id', $values, $errors) ?>
--------------------------------------------------------------------------------
/Template/user/create_remote.php:
--------------------------------------------------------------------------------
1 | = $this->form->label(t('OAuth2 ID'), 'oauth2_user_id') ?>
2 | = $this->form->text('oauth2_user_id', $values, $errors) ?>
--------------------------------------------------------------------------------
/Makefile:
--------------------------------------------------------------------------------
1 | plugin=OAuth2
2 |
3 | all:
4 | @ echo "Build archive for plugin ${plugin} version=${version}"
5 | @ git archive HEAD --prefix=${plugin}/ --format=zip -o ${plugin}-${version}.zip
6 |
--------------------------------------------------------------------------------
/Schema/Mysql.php:
--------------------------------------------------------------------------------
1 | exec('ALTER TABLE users ADD COLUMN oauth2_user_id VARCHAR(255)');
12 | }
13 |
--------------------------------------------------------------------------------
/Schema/Postgres.php:
--------------------------------------------------------------------------------
1 | exec('ALTER TABLE users ADD COLUMN oauth2_user_id VARCHAR(255)');
12 | }
13 |
--------------------------------------------------------------------------------
/Schema/Sqlite.php:
--------------------------------------------------------------------------------
1 | exec('ALTER TABLE users ADD COLUMN oauth2_user_id VARCHAR(255)');
12 | }
13 |
--------------------------------------------------------------------------------
/.github/issue_template.md:
--------------------------------------------------------------------------------
1 | **Please, do not create duplicate issues**
2 |
3 |
4 | ### Actual behaviour
5 |
6 |
7 | ### Expected behaviour
8 |
9 |
10 | ### Steps to reproduce
11 |
12 |
13 | ### Configuration
14 |
15 | - Plugin version:
16 | - Kanboard version:
17 | - Database type and version:
18 | - PHP version:
19 | - OS:
20 | - Browser:
21 |
22 |
--------------------------------------------------------------------------------
/Template/auth/login.php:
--------------------------------------------------------------------------------
1 |
4 | user->isCurrentUser($user['id'])): ?>
5 |
6 | = $this->url->link(t('Link OAuth2 account'), 'OAuthController', 'handler', array('plugin' => 'OAuth2'), true) ?>
7 |
8 | = $this->url->link(t('Unlink my OAuth2 account'), 'OAuthController', 'unlink', array('backend' => 'OAuth2'), true) ?>
9 |
10 |
11 | = empty($user['oauth2_user_id']) ? t('No account linked.') : t('Account linked.') ?>
12 |
13 |
14 |
--------------------------------------------------------------------------------
/Test/PluginTest.php:
--------------------------------------------------------------------------------
1 | container);
14 | $this->assertSame(null, $plugin->initialize());
15 | $this->assertSame(null, $plugin->onStartup());
16 | $this->assertNotEmpty($plugin->getPluginName());
17 | $this->assertNotEmpty($plugin->getPluginDescription());
18 | $this->assertNotEmpty($plugin->getPluginAuthor());
19 | $this->assertNotEmpty($plugin->getPluginVersion());
20 | $this->assertNotEmpty($plugin->getPluginHomepage());
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2016 Frédéric Guillot
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 |
--------------------------------------------------------------------------------
/Plugin.php:
--------------------------------------------------------------------------------
1 | authenticationManager->register(new GenericOAuth2Provider($this->container));
15 | $this->applicationAccessMap->add('OAuthController', 'handler', Role::APP_PUBLIC);
16 |
17 | $this->route->addRoute('/oauth/callback', 'OAuthController', 'handler', 'OAuth2');
18 |
19 | $this->template->hook->attach('template:auth:login-form:after', 'OAuth2:auth/login', [
20 | 'oauth2_custom_login_text' => $this->configModel->get('oauth2_custom_login_text'),
21 | ]);
22 | $this->template->hook->attach('template:config:integrations', 'OAuth2:config/integration');
23 | $this->template->hook->attach('template:user:external', 'OAuth2:user/external');
24 | $this->template->hook->attach('template:user:authentication:form', 'OAuth2:user/authentication');
25 | $this->template->hook->attach('template:user:create-remote:form', 'OAuth2:user/create_remote');
26 | }
27 |
28 | public function onStartup()
29 | {
30 | Translator::load($this->languageModel->getCurrentLanguage(), __DIR__.'/Locale');
31 | }
32 |
33 | public function getPluginName()
34 | {
35 | return 'OAuth2';
36 | }
37 |
38 | public function getPluginDescription()
39 | {
40 | return t('Generic OAuth2 authentication plugin');
41 | }
42 |
43 | public function getPluginAuthor()
44 | {
45 | return 'Frédéric Guillot';
46 | }
47 |
48 | public function getPluginVersion()
49 | {
50 | return '1.0.2';
51 | }
52 |
53 | public function getPluginHomepage()
54 | {
55 | return 'https://github.com/kanboard/plugin-oauth2';
56 | }
57 |
58 | public function getCompatibleVersion()
59 | {
60 | return '>=1.0.37';
61 | }
62 | }
63 |
64 |
--------------------------------------------------------------------------------
/.github/workflows/unit_tests.yml:
--------------------------------------------------------------------------------
1 | name: Unit Tests
2 |
3 | on:
4 | pull_request:
5 | branches: [ master ]
6 |
7 | jobs:
8 | Sqlite:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: Checkout Kanboard repo
12 | uses: actions/checkout@v2
13 | with:
14 | repository: kanboard/kanboard
15 | - name: Checkout Plugin repo
16 | uses: actions/checkout@v2
17 | with:
18 | path: plugins/OAuth2
19 | - name: Install dependencies
20 | run: composer install --prefer-dist --no-progress --no-suggest
21 | - name: Unit tests with Sqlite
22 | run: ./vendor/bin/phpunit -c tests/units.sqlite.xml plugins/OAuth2/Test/
23 |
24 | Postgres:
25 | runs-on: ubuntu-latest
26 | services:
27 | postgres:
28 | image: postgres:9.4
29 | env:
30 | POSTGRES_USER: postgres
31 | POSTGRES_PASSWORD: postgres
32 | POSTGRES_DB: kanboard_unit_test
33 | ports:
34 | - 5432:5432
35 | options: >-
36 | --health-cmd pg_isready
37 | --health-interval 10s
38 | --health-timeout 5s
39 | --health-retries 5
40 | steps:
41 | - name: Checkout Kanboard repo
42 | uses: actions/checkout@v2
43 | with:
44 | repository: kanboard/kanboard
45 | - name: Checkout Plugin repo
46 | uses: actions/checkout@v2
47 | with:
48 | path: plugins/OAuth2
49 | - name: Install dependencies
50 | run: composer install --prefer-dist --no-progress --no-suggest
51 | - name: Unit tests with Postgres
52 | run: ./vendor/bin/phpunit -c tests/units.postgres.xml plugins/OAuth2/Test/
53 | env:
54 | DB_HOSTNAME: 127.0.0.1
55 | DB_PORT: ${{ job.services.postgres.ports[5432] }}
56 |
57 | MySQL:
58 | runs-on: ubuntu-latest
59 | services:
60 | mysql:
61 | image: mysql:5.7
62 | env:
63 | MYSQL_ROOT_PASSWORD: "kanboard"
64 | MYSQL_DATABASE: "kanboard_unit_test"
65 | MYSQL_USER: "kanboard"
66 | MYSQL_PASSWORD: "kanboard"
67 | ports:
68 | - 3306:3306
69 | options: >-
70 | --health-cmd="mysqladmin ping"
71 | --health-interval 10s
72 | --health-timeout 5s
73 | --health-retries 10
74 | steps:
75 | - name: Checkout Kanboard repo
76 | uses: actions/checkout@v2
77 | with:
78 | repository: kanboard/kanboard
79 | - name: Checkout Plugin repo
80 | uses: actions/checkout@v2
81 | with:
82 | path: plugins/OAuth2
83 | - name: Install dependencies
84 | run: composer install --prefer-dist --no-progress --no-suggest
85 | - name: Unit tests with MySQL
86 | run: ./vendor/bin/phpunit -c tests/units.mysql.xml plugins/OAuth2/Test/
87 | env:
88 | DB_HOSTNAME: 127.0.0.1
89 | DB_USERNAME: kanboard
90 | DB_PASSWORD: kanboard
91 | DB_NAME: kanboard_unit_test
92 | DB_PORT: ${{ job.services.mysql.ports[3306] }}
93 |
--------------------------------------------------------------------------------
/Template/config/integration.php:
--------------------------------------------------------------------------------
1 |
3 | = $this->form->label(t('Callback URL'), 'oauth2_callback_url') ?>
4 |
5 |
6 | = $this->form->label(t('Client ID'), 'oauth2_client_id') ?>
7 | = $this->form->password('oauth2_client_id', $values) ?>
8 |
9 | = $this->form->label(t('Client Secret'), 'oauth2_client_secret') ?>
10 | = $this->form->password('oauth2_client_secret', $values) ?>
11 |
12 | = $this->form->label(t('Authorize URL'), 'oauth2_authorize_url') ?>
13 | = $this->form->text('oauth2_authorize_url', $values) ?>
14 |
15 | = $this->form->label(t('Token URL'), 'oauth2_token_url') ?>
16 | = $this->form->text('oauth2_token_url', $values) ?>
17 |
18 | = $this->form->label(t('User API URL'), 'oauth2_user_api_url') ?>
19 | = $this->form->text('oauth2_user_api_url', $values) ?>
20 |
21 | = $this->form->label(t('Scopes'), 'oauth2_scopes') ?>
22 | = $this->form->text('oauth2_scopes', $values) ?>
23 |
24 | = $this->form->label(t('Username Key'), 'oauth2_key_username') ?>
25 | = $this->form->text('oauth2_key_username', $values) ?>
26 |
27 | = $this->form->label(t('Name Key'), 'oauth2_key_name') ?>
28 | = $this->form->text('oauth2_key_name', $values) ?>
29 |
30 | = $this->form->label(t('Email Key'), 'oauth2_key_email') ?>
31 | = $this->form->text('oauth2_key_email', $values) ?>
32 |
33 | = $this->form->label(t('User ID Key'), 'oauth2_key_user_id') ?>
34 | = $this->form->text('oauth2_key_user_id', $values) ?>
35 |
36 | = $this->form->hidden('oauth2_split_keys', array('oauth2_split_keys' => 0)) ?>
37 | = $this->form->checkbox('oauth2_split_keys', t('Use composite keys?'), 1, isset($values['oauth2_split_keys']) && $values['oauth2_split_keys'] == 1) ?>
38 |
= t('Interpret \'.\' in keys as keys for sub-objects') ?>
39 |
40 | = $this->form->hidden('oauth2_account_creation', array('oauth2_account_creation' => 0)) ?>
41 | = $this->form->checkbox('oauth2_account_creation', t('Allow Account Creation'), 1, isset($values['oauth2_account_creation']) && $values['oauth2_account_creation'] == 1) ?>
42 |
43 | = $this->form->label(t('Allow account creation only for those domains'), 'oauth2_email_domains') ?>
44 | = $this->form->text('oauth2_email_domains', $values) ?>
45 |
= t('Use a comma to enter multiple domains: domain1.tld, domain2.tld') ?>
46 |
47 | = $this->form->label(t('Groups Key'), 'oauth2_key_groups') ?>
48 | = $this->form->text('oauth2_key_groups', $values) ?>
49 |
= t('Leave empty, when no group mapping is wanted') ?>
50 |
51 | = $this->form->label(t('Group Filter'), 'oauth2_key_group_filter') ?>
52 | = $this->form->text('oauth2_key_group_filter', $values) ?>
53 |
= t('Use a comma to enter multiple useable groups: group1,group2') ?>
54 |
55 | = $this->form->label(t('Automatically add user to a group'), 'oauth2_custom_group') ?>
56 | = $this->form->text('oauth2_custom_group', $values) ?>
57 |
= t('Use a comma to enter multiple useable groups: group1,group2 (The referenced groups are handled as external, so existing groups might not work as expected)') ?>
58 |
59 | = $this->form->label(t('Custom Login Text'), 'oauth2_custom_login_text') ?>
60 | = $this->form->text('oauth2_custom_login_text', $values) ?>
61 |
= t('Enter the text you would prefer to see rather than the default "OAuth2 login".') ?>
62 |
63 |
64 |
65 |
66 |
67 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | OAuth2 Authentication
2 | =====================
3 |
4 | Generic OAuth2 authentication plugin.
5 |
6 | Author
7 | ------
8 |
9 | - Frédéric Guillot
10 | - License MIT
11 |
12 | Requirements
13 | ------------
14 |
15 | - Kanboard >= 1.0.37
16 |
17 | Installation
18 | ------------
19 |
20 | You have the choice between 3 methods:
21 |
22 | 1. Install the plugin from the Kanboard plugin manager in one click
23 | 2. Download the zip file and decompress everything under the directory `plugins/OAuth2`
24 | 3. Clone this repository into the folder `plugins/OAuth2`
25 |
26 | Note: Plugin folder is case-sensitive.
27 |
28 | Configuration
29 | -------------
30 |
31 | > **Note:** Also works with most OpenID Providers
32 |
33 | Go to the application settings > integrations > OAuth2 Authentication.
34 |
35 | ### 1) Create a new application on the OAuth2 provider
36 |
37 | Go to the third-party authentication provider and add a new application.
38 | Copy and paste the **Kanboard callback URL** and generate a new set of tokens.
39 |
40 | The third-party provider will returns a **Client ID** and a **Client Secret**.
41 | Copy those values in the Kanboard's settings.
42 |
43 | ### 2) Configure the provider in Kanboard
44 |
45 | - **Client ID**: Unique ID that comes from the third-party provider
46 | - **Client Secret**: Unique token that comes from the third-party provider
47 | - **Authorize URL**: URL used for authorization
48 | - **Token URL**: URL used to get tokens from third-party provider
49 | - **User API URL**: URL used to fetch user profile after authentication
50 | - **Username Key**: Key used to fetch the username from the user API response
51 | - **Name Key**: Key used to fetch the full name
52 | - **Email Key**: Key used to fetch the user email
53 | - **User ID Key**: Key used to fetch the unique user ID
54 |
55 | Notes
56 | -----
57 |
58 | If "Allow Account Creation" checkbox is checked, anyone who goes to the login page, clicks the
59 | "OAuth2 Login" link, and correctly validates with your Oauth2 backend will automatically have
60 | their account created. No need to create the user in Kanboard or to use the "Invite people"
61 | link in the users area.
62 |
63 | Examples
64 | --------
65 |
66 | Example for Github OAuth2:
67 |
68 | - **Authorize URL**: `https://github.com/login/oauth/authorize`
69 | - **Token URL**: `https://github.com/login/oauth/access_token`
70 | - **User API URL**: `https://api.github.com/user`
71 | - **Username Key**: `login`
72 | - **Name Key**: `name`
73 | - **Email Key**: `email`
74 | - **User ID Key**: `id`
75 |
76 | Example for Salesforce:
77 |
78 | - **Authorize URL**: `https://login.salesforce.com/services/oauth2/authorize`
79 | - **Token URL**: `https://login.salesforce.com/services/oauth2/token`
80 | - **User API URL**: `https://login.salesforce.com/services/oauth2/userinfo`
81 | - **Username Key**: `nickname`
82 | - **Name Key**: `name`
83 | - **Email Key**: `email`
84 | - **User ID Key**: `user_id`
85 |
86 | Example for Discord:
87 |
88 | - **Authorize URL**: `https://discord.com/api/oauth2/authorize`
89 | - **Token URL**: `https://discord.com/api/oauth2/token`
90 | - **User API URL**: `https://discordapp.com/api/users/@me`
91 | - **Scopes**: `email identify`
92 | - **Username Key**: `username`
93 | - **Name Key**: `username`
94 | - **Email Key**: `email`
95 | - **User ID Key**: `id`
96 |
97 | Example for Gitea:
98 |
99 | - **Authorize URL**: `https://try.gitea.io/login/oauth/authorize`
100 | - **Token URL**: `https://try.gitea.io/login/oauth/access_token`
101 | - **User API URL**: `https://try.gitea.io/login/oauth/userinfo`
102 | - **Scopes**: `openid profile email groups`
103 | - **Username Key**: `preferred_username`
104 | - **Name Key**: `name`
105 | - **Email Key**: `email`
106 | - **User ID Key**: `sub`
107 |
108 | Example for Slack:
109 |
110 | - **Authorize URL**: `https://slack.com/openid/connect/authorize`
111 | - **Token URL**: `https://slack.com/api/openid.connect.token`
112 | - **User API URL**: `https://slack.com/api/openid.connect.userInfo`
113 | - **Scopes**: `openid profile email`
114 | - **Username Key**: `name`
115 | - **Name Key**: `name`
116 | - **Email Key**: `email`
117 | - **User ID Key**: `sub`
118 |
119 | Example for Azure AD (find the URLs with proper UUIDs in your Azure app page):
120 |
121 | - **Authorize URL**: `https://login.microsoftonline.com/