Skip to content
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 OAuth based authentication to Zoom plugin #52

Merged
merged 25 commits into from
Dec 12, 2019
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 22 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ You will need a paid Zoom account to use the plugin.

1. Go to **System Console > Plugins > Zoom** to configure the Zoom Plugin.

![image](https://github.com/mattermost/docs/raw/master/source/images/zoom_system_console.png)
![image](./assets/settings.png)

2. If you're using a self-hosted private cloud or on-premise Zoom server, enter the **Zoom URL** and **Zoom API URL** for the Zoom server, for example `https://yourzoom.com` and `https://api.yourzoom.com/v2` respectively. Leave blank if you're using Zoom's vendor-hosted SaaS service.

Expand All @@ -37,9 +37,28 @@ You will need a paid Zoom account to use the plugin.

To generate an **API Key** and **API Secret** requires a [Pro, Business, Education, or API Zoom plan](https://zoom.us/pricing).

4. Enable settings for [overriding usernames](https://docs.mattermost.com/administration/config-settings.html#enable-integrations-to-override-usernames) and [overriding profile picture icons](https://docs.mattermost.com/administration/config-settings.html#enable-integrations-to-override-profile-picture-icons).
4. Set the **OAuth ClientID** and **OAuth Secret**, generated by Zoom and used to create meetings and pull user data:

5. Activate the plugin at **System Console > Plugins > Management** by clicking **Activate** for Zoom.
- Go to https://marketplace.zoom.us/ and log in.
- In the top left click on **Develop** and then **Build App**.
- Select **OAuth** in **Choose your app type** section.
- Enter a name for your app and disable **Intend to publish this app on Zoom Marketplace**.
- Choose **Account-level app** as the app type.
- Click **Create**.
- Enter the **Company Name** and **Developer Contact Information** for your app.
- Go to the **App Credentials** tab on the left. Here you'll find your **Client ID** and **Client Secret**.
- Enter a Valid **Redirect URL for OAuth** (`https://<SiteUrl>/plugins/zoom/oauth2/complete`) and add the same url under **Whitelist URL**.
* `SiteUrl` should be your mattermost server url
- Add following scopes "user:read", "meeting:write", "webinar:write", "recording:write"
- Paste the **Client ID** and **Client Secret** into the fields in the System Console, and hit **Save**.
- Generate an **Encryption Key** to save the encryped tokens.

![create OAuth app scrren](./assets/oauth_creds.png)


5. Enable settings for [overriding usernames](https://docs.mattermost.com/administration/config-settings.html#enable-integrations-to-override-usernames) and [overriding profile picture icons](https://docs.mattermost.com/administration/config-settings.html#enable-integrations-to-override-profile-picture-icons).

6. Activate the plugin at **System Console > Plugins > Management** by clicking **Activate** for Zoom.

![image](https://github.com/mattermost/docs/blob/master/source/images/zoom_system-console_management.png)

Expand Down
Binary file added assets/oauth_creds.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added assets/settings.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
1 change: 1 addition & 0 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ require (
go.uber.org/zap v1.10.0 // indirect
golang.org/x/crypto v0.0.0-20190701094942-4def268fd1a4 // indirect
golang.org/x/net v0.0.0-20190628185345-da137c7871d7 // indirect
golang.org/x/oauth2 v0.0.0-20190319182350-c85d3e98c914
cpoile marked this conversation as resolved.
Show resolved Hide resolved
golang.org/x/sys v0.0.0-20190626221950-04f50cda93cb // indirect
google.golang.org/appengine v1.6.1 // indirect
google.golang.org/genproto v0.0.0-20190708153700-3bdd9d9f5532 // indirect
Expand Down
1 change: 1 addition & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -338,6 +338,7 @@ golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAG
golang.org/x/oauth2 v0.0.0-20181017192945-9dcd33a902f4/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20181203162652-d668ce993890/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U=
golang.org/x/oauth2 v0.0.0-20190226205417-e64efc72b421/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/oauth2 v0.0.0-20190319182350-c85d3e98c914 h1:jIOcLT9BZzyJ9ce+IwwZ+aF9yeCqzrR+NrD68a/SHKw=
golang.org/x/oauth2 v0.0.0-20190319182350-c85d3e98c914/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw=
golang.org/x/perf v0.0.0-20180704124530-6e6d33e29852/go.mod h1:JLpeXjPJfIyPr5TlbXLkXWLhP8nz10XfvxElABhCtcw=
golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f h1:wMNYb4v58l5UBM7MYRLPG6ZhfOqbKu7X5eyFl8ZhKvA=
Expand Down
33 changes: 33 additions & 0 deletions plugin.json
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,39 @@
"help_text": "The API URL for a self-hosted private cloud or on-premise Zoom server. For example, https://api.yourzoom.com/v2. Leave blank if you're using Zoom's vendor-hosted SaaS service.",
"placeholder": "https://api.zoom.us/v2"
},
{
"key": "EnableOAuth",
"display_name": "Enable OAuth",
"type": "bool",
"help_text": "When true, OAuth will be used as authentication means with Zoom. \n Please enable only either one of OAuth based or Password base authentication.",
"default": false
},
{
"key": "OAuthClientID",
"display_name": "Zoom OAuth Client ID",
"type": "text",
"help_text": "The Client ID for the OAuth app registered with Zoom. Leave blank if not using OAuth"
},
{
"key": "OAuthClientSecret",
"display_name": "Zoom OAuth Client Secret",
"type": "text",
"help_text": "The Client Secret for the OAuth app registered with Zoom. Leave blank if not using OAuth"
},
{
"key": "EncryptionKey",
"display_name": "At Rest Token Encryption Key",
"type": "generated",
"help_text": "The AES encryption key used to encrypt stored access tokens.",
"regenerate_help_text": "Regenerates the encryption key for Zoom OAuth Token. Regenerating the key invalidates your existing Zoom OAuth."
},
{
"key": "EnableLegacyAuth",
"display_name": "Enable Password based authentication",
"type": "bool",
"help_text": "When true, user's email and password will be used to authenticate with Zoom. \n Please enable only either one of OAuth based or Password base authentication.",
"default": true
},
{
"key": "APIKey",
"display_name": "API Key",
Expand Down
33 changes: 5 additions & 28 deletions server/command.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import (
"fmt"
"strings"

"github.com/mattermost/mattermost-plugin-zoom/server/zoom"
"github.com/mattermost/mattermost-server/model"
"github.com/mattermost/mattermost-server/plugin"
)
Expand Down Expand Up @@ -32,8 +31,6 @@ func (p *Plugin) postCommandResponse(args *model.CommandArgs, text string) {
}

func (p *Plugin) executeCommand(c *plugin.Context, args *model.CommandArgs) (string, error) {
config := p.getConfiguration()

split := strings.Fields(args.Command)
command := split[0]
action := ""
Expand All @@ -59,34 +56,14 @@ func (p *Plugin) executeCommand(c *plugin.Context, args *model.CommandArgs) (str
return fmt.Sprintf("We could not get channel members (channelId: %v)", args.ChannelId), nil
}

// create a personal zoom meeting
ru, clientErr := p.zoomClient.GetUser(user.Email)
if clientErr != nil {
return "We could not verify your Mattermost account in Zoom. Please ensure that your Mattermost email address matches your Zoom login email address.", nil
}
meetingID := ru.Pmi

zoomURL := strings.TrimSpace(config.ZoomURL)
if len(zoomURL) == 0 {
zoomURL = "https://zoom.us"
zoomUser, authErr := p.authenticateAndFetchZoomUser(userID, user.Email, args.ChannelId)
if authErr != nil {
return authErr.Message, authErr.Err
}

meetingURL := fmt.Sprintf("%s/j/%v", zoomURL, meetingID)

post := &model.Post{
UserId: p.botUserID,
ChannelId: args.ChannelId,
Message: fmt.Sprintf("Meeting started at %s.", meetingURL),
Type: "custom_zoom",
Props: map[string]interface{}{
"meeting_id": meetingID,
"meeting_link": meetingURL,
"meeting_status": zoom.WebhookStatusStarted,
"meeting_personal": true,
},
}
meetingID := zoomUser.Pmi

_, appErr := p.API.CreatePost(post)
_, appErr := p.postMeeting(meetingID, args.ChannelId, "")
if appErr != nil {
return "Failed to post message. Please try again.", nil
}
Expand Down
49 changes: 40 additions & 9 deletions server/configuration.go
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,17 @@ import (
// If you add non-reference types to your configuration struct, be sure to rewrite Clone as a deep
// copy appropriate for your types.
type configuration struct {
ZoomURL string
ZoomAPIURL string
APIKey string
APISecret string
WebhookSecret string
ZoomURL string
ZoomAPIURL string
EnableLegacyAuth bool
APIKey string
APISecret string
EnableOAuth bool
OAuthClientID string
OAuthClientSecret string
OAuthRedirectUrl string
EncryptionKey string
WebhookSecret string
}

// Clone shallow copies the configuration. Your implementation may require a deep copy if
Expand All @@ -37,12 +43,33 @@ func (c *configuration) Clone() *configuration {

// IsValid checks if all needed fields are set.
func (c *configuration) IsValid() error {
if len(c.APIKey) == 0 {
return errors.New("APIKey is not configured")

gopheros marked this conversation as resolved.
Show resolved Hide resolved
if c.EnableLegacyAuth && c.EnableOAuth {
return errors.New("Only enable One of the OAuth or Password based authentication")
}

if len(c.APISecret) == 0 {
return errors.New("APISecret is not configured")
switch {
case c.EnableLegacyAuth:
switch {
case len(c.APIKey) == 0:
return errors.New("APIKey is not configured")

case len(c.APISecret) == 0:
return errors.New("APISecret is not configured")
}
case c.EnableOAuth:
switch {
case len(c.OAuthClientSecret) == 0:
return errors.New("OAuthClientSecret is not configured")

case len(c.OAuthClientID) == 0:
return errors.New("OAuthClientID is not configured")

case len(c.EncryptionKey) == 0:
return errors.New("Please generate EncryptionKey from Zoom plugin settings")
}
default:
return errors.New("Please select either OAuth or Password based authentication")
}

if len(c.WebhookSecret) == 0 {
Expand Down Expand Up @@ -102,6 +129,10 @@ func (p *Plugin) OnConfigurationChange() error {
return errors.Wrap(err, "failed to load plugin configuration")
}

if configuration.EnableLegacyAuth && configuration.EnableOAuth {
return errors.New("Only enable One of the OAuth or Password based authentication")
}

p.setConfiguration(configuration)

return nil
Expand Down
Loading