The App framework provides support for authenticating with remote services using OAuth2, including storage of app secrets and user tokens.
There are three operations that can be performed:
The operations can be performed in two ways:
The OAuth2 authentication flow is initiated by the user invoking an App call, usually from a slash command or form. The call requires that the oauth2_app
and oauth2_user
context fields are expanded with expand level all
.
The App returns a URL that redirects the user to the App’s /oauth2/connect
endpoint, where a URL to the OAuth2 provider is returned, redirecting the user to continue the flow.
When the user has completed their authentication with the remote provider, they are redirected to the App’s /oauth2/complete
endpoint to complete the flow.
OAuth2 data is managed using a single HTTP REST endpoint:
<mattermost_site_url>/plugins/com.mattermost.apps/api/v1/oauth2
Replace <mattermost_site_url>
with the base URL to the Mattermost server.
The <mattermost_site_url>
value can be obtained from a call request context.
An authorization token is required when invoking HTTP REST endpoints for OAuth2. The token must be set in the Authorization
header as a bearer token. The bot_access_token
or acting_user_access_token
field of the call request context will contain the token; either token can be used.
Example HTTP request to register the client ID and secret for a remote OAuth2 provider:
POST /plugins/com.mattermost.apps/api/v1/oauth2/app HTTP/1.1
Authorization: Bearer xxxxxxxxxxxx
Content-Type: application/json
{
"client_id": "xxxxxxxxxxxx",
"client_secret": "xxxxxxxxxxxx"
}
Example HTTP request to set a user access token:
POST /plugins/com.mattermost.apps/api/v1/oauth2/user HTTP/1.1
Authorization: Bearer xxxxxxxxxxxx
Content-Type: application/json
{
"token": "xxxxxxxxxxxx"
}
Example HTTP request to get a user access token:
GET /plugins/com.mattermost.apps/api/v1/oauth2/user HTTP/1.1
Authorization: Bearer xxxxxxxxxxxx
Accept: application/json
Example response:
{
"token": "xxxxxxxxxxxx"
}
Example Golang driver code to register the OAuth2 provider client ID and client secret:
// configure is the App call invoked by the system administrator to register OAuth2 provider details.
// The call should be configured to accept two values: client_id and client_secret.
func configure(w http.ResponseWriter, req *http.Request) {
// Decode the call request data
creq := new(apps.CallRequest)
err := json.NewDecoder(req.Body).Decode(creq)
if err != nil {
// handle the error
}
// Read the client_id and client_secret values from the request
clientID, ok := creq.Values["client_id"].(string)
if !ok {
// handle the error
}
clientSecret, ok := creq.Values["client_secret"].(string)
if !ok {
// handle the error
}
// Get an instance of the API client as the acting user
asUser := appclient.AsActingUser(creq.Context)
// Register the OAuth2 provider's details (client ID and client secret)
err = asUser.StoreOAuth2App(apps.OAuth2App{
ClientID: clientID,
ClientSecret: clientSecret,
})
if err != nil {
// handle the error
}
// Respond with a success message
err = json.NewEncoder(w).Encode(
apps.NewTextResponse("updated OAuth client credentials"),
)
if err != nil {
// handle the error
}
}
Example Golang driver code to set a user access token:
// oauth2Complete is the App call invoked at the end of an OAuth2 authorization flow
func oauth2Complete(w http.ResponseWriter, req *http.Request) {
// Decode the call request data
callRequest := new(apps.CallRequest)
err := json.NewDecoder(req.Body).Decode(callRequest)
if err != nil {
// handle the error
}
// Read the authorization code from the call request values
authCode, ok := callRequest.Values["code"].(string)
if !ok {
// handle the error
}
// Exchange the authorization code for a token
token, err := oauth2Config(callRequest).Exchange(context.Background(), authCode)
if err != nil {
// handle the error
}
// Get an instance of the API client as the acting user
client := appclient.AsActingUser(callRequest.Context)
// Store the OAuth2 token for the acting user
err = client.StoreOAuth2User(token)
if err != nil {
// handle the error
}
// Return a success response
err = httputils.WriteJSON(w, apps.NewDataResponse(nil))
if err != nil {
// handle the error
}
}
The Golang oauth2Config
function referenced in the example above is responsible for creating a oauth2.Config
(godoc) struct which contains the necessary information to exchange an authorization code for a token.
Example Golang code for the oauth2Config
function referencing a Google OAuth2 provider:
func oauth2Config(creq *apps.CallRequest) *oauth2.Config {
return &oauth2.Config{
ClientID: creq.Context.OAuth2.ClientID,
ClientSecret: creq.Context.OAuth2.ClientSecret,
Endpoint: google.Endpoint,
RedirectURL: creq.Context.OAuth2.CompleteURL,
Scopes: []string{
"https://www.googleapis.com/auth/calendar",
"https://www.googleapis.com/auth/userinfo.profile",
"https://www.googleapis.com/auth/userinfo.email",
},
}
}
Example Golang driver code to get a user access token:
func useRemoteService(w http.ResponseWriter, req *http.Request) {
// Decode the call request data
callRequest := new(apps.CallRequest)
err := json.NewDecoder(req.Body).Decode(callRequest)
if err != nil {
// handle the error
}
// Get an instance of the API client as the acting user
client := appclient.AsActingUser(callRequest.Context)
// Get the user's token
var userToken string
err = client.GetOAuth2User(&userToken)
if err != nil {
// handle the error
}
// Use the token
// ...
// Return a success response
err = httputils.WriteJSON(w, apps.NewDataResponse(nil))
if err != nil {
// handle the error
}
}