Add support for logging in with OAuth

This commit is contained in:
JC Brand 2018-06-12 17:01:10 +02:00
parent a2dec2371d
commit 3dac4ae45c
15 changed files with 6689 additions and 34 deletions

View File

@ -20,6 +20,7 @@
If the device is not trusted, sessionStorage is used and all user data is deleted from the browser cache upon logout.
If the device is trusted, localStorage is used and user data is cached indefinitely.
- Initial support for XEP-0357 Push Notifications, specifically registering an "App Server".
- Add support for logging in via OAuth (see the [oauth_providers](https://conversejs.org/docs/html/configurations.html#oauth-providers) setting)
### Bugfixes

View File

@ -6947,13 +6947,17 @@ body.reset {
#conversejs ul, #conversejs ol, #conversejs dl {
font: inherit;
margin: 0; }
#conversejs a, #conversejs a:visited, #conversejs a:hover, #conversejs a:not([href]):not([tabindex]) {
#conversejs a, #conversejs a:visited, #conversejs a:not([href]):not([tabindex]) {
text-decoration: none;
color: #578EA9;
text-shadow: none; }
#conversejs a.fa, #conversejs a:visited.fa, #conversejs a:hover.fa, #conversejs a:not([href]):not([tabindex]).fa {
#conversejs a:hover, #conversejs a:visited:hover, #conversejs a:not([href]):not([tabindex]):hover {
color: #345566;
text-decoration: none;
text-shadow: none; }
#conversejs a.fa, #conversejs a:visited.fa, #conversejs a:not([href]):not([tabindex]).fa {
color: #A8ABA1; }
#conversejs a.fa:hover, #conversejs a:visited.fa:hover, #conversejs a:hover.fa:hover, #conversejs a:not([href]):not([tabindex]).fa:hover {
#conversejs a.fa:hover, #conversejs a:visited.fa:hover, #conversejs a:not([href]):not([tabindex]).fa:hover {
color: #818479; }
#conversejs canvas {
border-radius: 4px; }
@ -7803,6 +7807,20 @@ body.reset {
#conversejs.converse-fullscreen .chatbox .box-flyout .chatbox-buttons {
flex: 0 0 25%;
max-width: 25%; } }
#conversejs .oauth-providers {
text-align: center; }
#conversejs .oauth-providers .oauth-provider {
margin: 1em 0; }
#conversejs .oauth-providers .oauth-provider .oauth-login {
margin-left: 0;
color: #578EA9;
font-size: 16px; }
#conversejs .oauth-providers .oauth-provider .oauth-login:hover {
color: #345566; }
#conversejs .oauth-providers .oauth-provider .oauth-login i {
color: #578EA9;
font-size: 20px;
margin-right: 0.5em; }
#conversejs .set-xmpp-status .fa-circle, #conversejs .xmpp-status .fa-circle, #conversejs .roster-contacts .fa-circle {
color: #3AA569; }
#conversejs .set-xmpp-status .fa-minus-circle, #conversejs .xmpp-status .fa-minus-circle, #conversejs .roster-contacts .fa-minus-circle {
@ -7921,11 +7939,6 @@ body.reset {
color: #578EA9; }
#conversejs #controlbox .toggle-register-login {
font-weight: bold; }
#conversejs #controlbox .oauth-login {
margin-left: 0;
color: #666; }
#conversejs #controlbox .oauth-login .icon-social:before {
font-size: 16px; }
#conversejs #controlbox .controlbox-pane .userinfo {
padding-bottom: 1em; }
#conversejs #controlbox .controlbox-pane .userinfo .username {
@ -8000,6 +8013,7 @@ body.reset {
#conversejs #controlbox .controlbox-pane .chatbox-btn {
margin: 0; }
#conversejs #controlbox .controlbox-pane .switch-form {
text-align: center;
padding: 2em 0; }
#conversejs #controlbox .controlbox-pane .switch-form p {
margin-top: 0.5em; }

6418
dist/converse.js vendored

File diff suppressed because it is too large Load Diff

View File

@ -986,6 +986,39 @@ This option specifies which icon is shown in HTML5 notifications, as provided
by the ``src/converse-notification.js`` plugin.
oauth_providers
---------------
* Default: ``[]``
Allows you to specify a list of OAuth providers that the user may use to log in
with.
.. note::
Your XMPP server will have to support Oauth logins
.. code-block:: javascript
converse.initialize({
oauth_providers: {
'github': {
'client_id': '1338d9f7ff52b1309b29',
'host': 'chat.example.org',
'class': 'fa-github-alt',
'id': 'github',
'name': 'Github'
},
'twitter': {
'client_id': '0332d98cff83b1999b22',
'host': 'chat.example.org',
'class': 'fa-twitter',
'id': 'twitter',
'name': 'Twitter'
}
},
});
ping_interval
-------------
@ -1069,7 +1102,7 @@ The hyperlink on the registration form which points to a directory of public
XMPP servers.
push_app_servers
-------------
----------------
* Default: ``[]``

6
package-lock.json generated
View File

@ -6434,6 +6434,12 @@
"integrity": "sha1-k0EP0hsAlzUVH4howvJx80J+I/0=",
"dev": true
},
"hellojs": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/hellojs/-/hellojs-1.16.1.tgz",
"integrity": "sha1-rOoZ72LPr8WVF7N5vw+NnT9Dm9Q=",
"dev": true
},
"hmac-drbg": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz",

View File

@ -1,8 +1,8 @@
{
"name": "converse.js",
"version": "3.3.4",
"description": "Browser based XMPP instant messaging client",
"main": "main.js",
"description": "Browser based XMPP chat client",
"main": "dist/converse.js",
"directories": {
"doc": "docs",
"locale": "locale",
@ -20,10 +20,13 @@
"chatrooms",
"webchat"
],
"author": "JC Brand",
"author": {
"name": "JC Brand",
"email": "jc@opkode.com"
},
"license": "MPL-2.0",
"bugs": {
"url": "https://github.com/jcbrand/converse.js/issues"
"url": "https://github.com/conversejs/converse.js/issues"
},
"engines": {
"browser": "*"
@ -51,6 +54,7 @@
"exports-loader": "^0.7.0",
"filesize": "^3.6.1",
"font-awesome": "^4.7.0",
"hellojs": "^1.16.1",
"http-server": "^0.10.0",
"imports-loader": "^0.8.0",
"install": "^0.9.5",

18
redirect.html Normal file
View File

@ -0,0 +1,18 @@
<!doctype html>
<html class="no-js" lang="en">
<head>
<meta charset="utf-8"/>
<meta name="viewport" content="width=device-width, initial-scale=1.0"/>
<title>Converse</title>
<link rel="shortcut icon" type="image/ico" href="css/images/favicon.ico"/>
<link type="text/css" rel="stylesheet" media="screen" href="css/converse.css" />
<script src="node_modules/hellojs/dist/hello.all.js"></script>
</head>
<body>
<div class="content">
<div class="inner-content">
<h1 class="brand-heading"><i class="icon-conversejs"></i> Converse</h1>
</div>
</div>
</body>
</html>

View File

@ -1,4 +1,24 @@
#conversejs {
.oauth-providers {
text-align: center;
.oauth-provider {
margin: 1em 0;
.oauth-login {
margin-left: 0;
color: $link-color;
font-size: $font-size-large;
&:hover {
color: darken($link-color, 20%);
}
i {
color: $link-color;
font-size: $font-size-huge;
margin-right: 0.5em;
}
}
}
}
.set-xmpp-status, .xmpp-status, .roster-contacts {
.fa-circle {
@ -173,15 +193,6 @@
font-weight: bold;
}
.oauth-login {
margin-left: 0;
color: $text-color;
.icon-social:before {
font-size: $font-size-large;
}
}
.controlbox-pane {
.userinfo {
padding-bottom: 1em;
@ -296,6 +307,7 @@
}
.switch-form {
text-align: center;
padding: 2em 0;
p {
margin-top: 0.5em;

View File

@ -176,10 +176,16 @@ body.reset {
margin: 0;
}
a, a:visited, a:hover, a:not([href]):not([tabindex]) {
a, a:visited, a:not([href]):not([tabindex]) {
text-decoration: none;
color: $link-color;
text-shadow: none;
&:hover {
color: darken($link-color, 20%);
text-decoration: none;
text-shadow: none;
}
&.fa {
color: $subdued-color;
&:hover {

View File

@ -85,6 +85,7 @@
'converse-muc',
'converse-muc-views',
'converse-notification',
'converse-oauth',
'converse-ping',
'converse-profile',
'converse-push',

146
src/converse-oauth.js Normal file
View File

@ -0,0 +1,146 @@
// Converse.js
// https://conversejs.org
//
// Copyright (c) 2013-2018, the Converse.js developers
// Licensed under the Mozilla Public License (MPLv2)
(function (root, factory) {
if (typeof define === 'function' && define.amd) {
// AMD. Register as a module called "myplugin"
define(["converse-core", "templates/oauth_providers.html", "hellojs"], factory);
} else {
// Browser globals. If you're not using a module loader such as require.js,
// then this line below executes. Make sure that your plugin's <script> tag
// appears after the one from converse.js.
factory(converse);
}
}(this, function (converse, tpl_oauth_providers, hello) {
'use strict';
const _ = converse.env._,
Backbone = converse.env.Backbone,
Strophe = converse.env.Strophe;
// The following line registers your plugin.
converse.plugins.add("converse-oauth", {
/* Optional dependencies are other plugins which might be
* overridden or relied upon, and therefore need to be loaded before
* this plugin. They are called "optional" because they might not be
* available, in which case any overrides applicable to them will be
* ignored.
*
* NB: These plugins need to have already been loaded via require.js.
*
* It's possible to make optional dependencies non-optional.
* If the setting "strict_plugin_dependencies" is set to true,
* an error will be raised if the plugin is not found.
*/
'optional_dependencies': ['converse-register'],
/* If you want to override some function or a Backbone model or
* view defined elsewhere in converse.js, then you do that under
* the "overrides" namespace.
*/
'overrides': {
/* For example, the private *_converse* object has a
* method "onConnected". You can override that method as follows:
*/
'LoginPanel': {
insertOAuthProviders () {
const { _converse } = this.__super__;
if (_.isUndefined(this.oauth_providers_view)) {
this.oauth_providers_view =
new _converse.OAuthProvidersView({'model': _converse.oauth_providers});
this.oauth_providers_view.render();
this.el.querySelector('.buttons').insertAdjacentElement(
'afterend',
this.oauth_providers_view.el
);
}
this.oauth_providers_view.render();
},
render (cfg) {
const { _converse } = this.__super__;
const result = this.__super__.render.apply(this, arguments);
if (_converse.oauth_providers && !_converse.auto_login) {
this.insertOAuthProviders();
}
return result;
}
}
},
initialize () {
/* The initialize function gets called as soon as the plugin is
* loaded by converse.js's plugin machinery.
*/
const { _converse } = this,
{ __ } = _converse;
_converse.api.settings.update({
'oauth_providers': {},
});
_converse.OAuthProviders = Backbone.Collection.extend({
'sync': __.noop,
initialize () {
_.each(_converse.user_settings.oauth_providers, (provider) => {
const item = new Backbone.Model(_.extend(provider, {
'login_text': __('Log in with %1$s', provider.name)
}));
this.add(item, {'silent': true});
});
}
});
_converse.oauth_providers = new _converse.OAuthProviders();
_converse.OAuthProvidersView = Backbone.VDOMView.extend({
'events': {
'click .oauth-login': 'oauthLogin'
},
toHTML () {
return tpl_oauth_providers(
_.extend({
'_': _,
'__': _converse.__,
'providers': this.model.toJSON()
}));
},
fetchOAuthProfileDataAndLogin () {
this.oauth_service.api('me').then((profile) => {
const response = this.oauth_service.getAuthResponse();
_converse.api.user.login({
'jid': `${profile.name}@${this.provider.get('host')}`,
'password': response.access_token
});
});
},
oauthLogin (ev) {
ev.preventDefault();
const id = ev.target.getAttribute('data-id');
this.provider = _converse.oauth_providers.get(id);
this.oauth_service = hello(id);
const data = {};
data[id] = this.provider.get('client_id');
hello.init(data, {
'redirect_uri': '/redirect.html'
});
this.oauth_service.login().then(
() => this.fetchOAuthProfileDataAndLogin(),
(error) => _converse.log(error.error_message, Strophe.LogLevel.ERROR)
);
}
});
}
});
}));

View File

@ -67,7 +67,7 @@
if (_.isUndefined(this.registerlinkview)) {
this.registerlinkview = new _converse.RegisterLinkView({'model': this.model});
this.registerlinkview.render();
this.el.querySelector('.buttons').insertAdjacentElement('beforeend', this.registerlinkview.el);
this.el.querySelector('.buttons').insertAdjacentElement('afterend', this.registerlinkview.el);
}
this.registerlinkview.render();
},

View File

@ -22,10 +22,11 @@ if (typeof define !== 'undefined') {
"converse-muc-views",
"converse-muc-views", // Views related to MUC
"converse-notification", // HTML5 Notifications
"converse-oauth",
"converse-ping", // XEP-0199 XMPP Ping
"converse-roster",
"converse-register", // XEP-0077 In-band registration
"converse-roomslist", // Show currently open chat rooms
"converse-roster",
"converse-vcard", // XEP-0054 VCard-temp
/* END: Removable components */
], function (converse) {

View File

@ -0,0 +1,9 @@
<fieldset class="oauth-providers">
{[ o._.forEach(o.providers, function (provider) { ]}
<p class="oauth-provider">
<a class="oauth-login" href="#" data-id="{{provider.id}}">
<i class="fa {{ provider.class}}"></i>{{provider.login_text}}</span>
</a>
</p>
{[ }); ]}
</fieldset>

View File

@ -1,6 +1,6 @@
<div class="switch-form">
<fieldset class="switch-form">
{[ if (!o._converse.auto_login && o._converse.CONNECTION_STATUS[o.connection_status] !== 'CONNECTING') { ]}
<p>{{{ o.__("Don't have a chat account?") }}}</p>
<p><a class="register-account toggle-register-login" href="#converse/register">{{{o.__("Create an account")}}}</a></p>
{[ } ]}
</div>
</fieldset>