diff --git a/app/app/pods/auth/keycloak/route.js b/app/app/pods/auth/keycloak/route.js index baab93d0..3792d092 100644 --- a/app/app/pods/auth/keycloak/route.js +++ b/app/app/pods/auth/keycloak/route.js @@ -19,52 +19,43 @@ export default Ember.Route.extend({ localStorage: Ember.inject.service(), queryParams: { mode: { - refreshModel: false + refreshModel: true } }, + message: '', beforeModel(transition) { - this.set('mode', is.not.undefined(transition.queryParams.mode) ? transition.queryParams.mode : 'login'); - if (this.get('appMeta.authProvider') !== constants.AuthProvider.Keycloak) { - return; - } + return new Ember.RSVP.Promise((resolve) => { + this.set('mode', is.not.undefined(transition.queryParams.mode) ? transition.queryParams.mode : 'reject'); - if (this.get('mode') === 'reject') { - return; - } - - this.get('kcAuth').boot().then((kc) => { - if (!kc.authenticated) { - this.get('kcAuth').login().then(() => { - }, (reject) => { - this.get('localStorage').storeSessionItem('kc-error', reject); - this.set('mode', 'reject'); - }); + if (this.get('mode') === 'reject' || this.get('appMeta.authProvider') !== constants.AuthProvider.Keycloak) { + resolve(); } - this.get('kcAuth').fetchProfile(kc).then((profile) => { - let data = this.get('kcAuth').mapProfile(kc, profile); - this.get("session").authenticate('authenticator:keycloak', data).then(() => { - this.get('audit').record("logged-in-keycloak"); - this.transitionTo('folders'); - }, (reject) => { - this.get('localStorage').storeSessionItem('kc-error', reject); - this.set('mode', 'reject'); - }); + this.get('kcAuth').fetchProfile().then((profile) => { + let data = this.get('kcAuth').mapProfile(profile); - }, (reject) => { - this.get('localStorage').storeSessionItem('kc-error', reject); - this.set('mode', 'reject'); - }); - }, (reject) => { - this.get('localStorage').storeSessionItem('kc-error', reject); - this.set('mode', 'reject'); + this.get("session").authenticate('authenticator:keycloak', data).then(() => { + this.get('audit').record("logged-in-keycloak"); + this.transitionTo('folders'); + }, (reject) => { + this.set('message', reject.Error); + this.set('mode', 'reject'); + resolve(); + }); + + }, (reject) => { + this.set('mode', 'reject'); + this.set('message', reject); + resolve(); + }); }); }, model() { return { - mode: this.get('mode') + mode: this.get('mode'), + message: this.get('message') } } }); diff --git a/app/app/pods/auth/keycloak/template.hbs b/app/app/pods/auth/keycloak/template.hbs index c62a27ca..8a8701a2 100644 --- a/app/app/pods/auth/keycloak/template.hbs +++ b/app/app/pods/auth/keycloak/template.hbs @@ -7,6 +7,7 @@ {{#if (is-equal model.mode 'reject')}}
-

Keycloak Authentication Failure

+

Keycloak authentication failure

+

{{model.message}}

{{/if}} diff --git a/app/app/pods/auth/login/route.js b/app/app/pods/auth/login/route.js index 8401b4a4..8ea63576 100644 --- a/app/app/pods/auth/login/route.js +++ b/app/app/pods/auth/login/route.js @@ -18,30 +18,31 @@ export default Ember.Route.extend({ localStorage: Ember.inject.service(), showLogin: false, - beforeModel(/*transition*/) { - let authProvider = this.get('appMeta.authProvider'); + beforeModel(transition) { + return new Ember.RSVP.Promise((resolve) => { + let authProvider = this.get('appMeta.authProvider'); - switch (authProvider) { - case constants.AuthProvider.Keycloak: - this.set('showLogin', false); + switch (authProvider) { + case constants.AuthProvider.Keycloak: + this.set('showLogin', false); - this.get('kcAuth').boot().then(() => { this.get('kcAuth').login().then(() => { + this.transitionTo('auth.keycloak', { queryParams: { mode: 'login' }}); + resolve(); }, (reject) => { - this.get('localStorage').storeSessionItem('kc-error', reject); + transition.abort(); + console.log (reject); // eslint-disable-line no-console this.transitionTo('auth.keycloak', { queryParams: { mode: 'reject' }}); }); - }, (reject) => { - this.get('localStorage').storeSessionItem('kc-error', reject); - this.transitionTo('auth.keycloak', { queryParams: { mode: 'reject' }}); - }); - break; - - default: - this.set('showLogin', true); - break; - } + break; + + default: + this.set('showLogin', true); + resolve(); + break; + } + }); }, model() { diff --git a/app/app/services/app-meta.js b/app/app/services/app-meta.js index 4ed9dc35..52c2c8ab 100644 --- a/app/app/services/app-meta.js +++ b/app/app/services/app-meta.js @@ -22,7 +22,7 @@ const { export default Ember.Service.extend({ ajax: service(), localStorage: service(), - + kcAuth: service(), endpoint: `${config.apiHost}/${config.apiNamespace}`, orgId: '', title: '', @@ -64,6 +64,7 @@ export default Ember.Service.extend({ return this.get('ajax').request('public/meta').then((response) => { this.setProperties(response); + return response; }); } diff --git a/app/app/services/kc-auth.js b/app/app/services/kc-auth.js index 07356748..553941f1 100644 --- a/app/app/services/kc-auth.js +++ b/app/app/services/kc-auth.js @@ -22,16 +22,26 @@ export default Ember.Service.extend({ ajax: service(), appMeta: service(), keycloak: null, - - init () { - this._super(...arguments); - this.keycloak = null; - }, + config: {}, boot() { - this.set('keycloak', new Keycloak(JSON.parse(this.get('appMeta.authConfig')))); - return new Ember.RSVP.Promise((resolve, reject) => { + if (is.not.undefined(this.get('keycloak')) && is.not.null(this.get('keycloak')) ) { + resolve(this.get('keycloak')); + return; + } + + let keycloak = new Keycloak(JSON.parse(this.get('appMeta.authConfig'))); + this.set('keycloak', keycloak); + + keycloak.onTokenExpired = function () { + keycloak.clearToken(); + }; + + keycloak.onAuthRefreshError = function () { + keycloak.clearToken(); + }; + this.get('keycloak').init().success(() => { this.get('audit').record("initialized-keycloak"); resolve(this.get('keycloak')); @@ -42,12 +52,11 @@ export default Ember.Service.extend({ }, login() { - this.set('keycloak', new Keycloak(JSON.parse(this.get('appMeta.authConfig')))); - let url = netUtil.getAppUrl(netUtil.getSubdomain()) + '/auth/keycloak?mode=login'; - return new Ember.RSVP.Promise((resolve, reject) => { - this.boot().then(() => { - this.get('keycloak').login({redirectUri: url}).success(() => { + this.boot().then((keycloak) => { + let url = netUtil.getAppUrl(netUtil.getSubdomain()) + '/auth/keycloak?mode=login'; + + keycloak.login({redirectUri: url}).success(() => { return resolve(); }).error(() => { return reject(new Error('login failed')); @@ -57,37 +66,35 @@ export default Ember.Service.extend({ }, logout() { - this.set('keycloak', new Keycloak(JSON.parse(this.get('appMeta.authConfig')))); - return new Ember.RSVP.Promise((resolve, reject) => { - this.boot().then(() => { - this.get('keycloak').logout(JSON.parse(this.get('appMeta.authConfig'))).success(() => { + this.boot().then((keycloak) => { + keycloak.logout(JSON.parse(this.get('appMeta.authConfig'))).success(() => { this.get('keycloak').clearToken(); resolve(); }).error((error) => { this.get('keycloak').clearToken(); reject(error); }); - }, (error) => { - reject(error); }); }); }, - fetchProfile(kc) { + fetchProfile() { return new Ember.RSVP.Promise((resolve, reject) => { - kc.loadUserProfile().success((profile) => { - return resolve(profile); - }).error((err) => { - return reject(err); + this.boot().then((keycloak) => { + keycloak.loadUserProfile().success((profile) => { + resolve(profile); + }).error((err) => { + reject(err); + }); }); }); }, - mapProfile(kc, profile) { + mapProfile(profile) { return { domain: '', - token: kc.token, + token: this.get('keycloak').token, remoteId: is.null(profile.id) || is.undefined(profile.id) ? profile.email: profile.id, email: is.null(profile.email) || is.undefined(profile.email) ? '': profile.email, username: is.null(profile.username) || is.undefined(profile.username) ? '': profile.username, diff --git a/core/api/endpoint/keycloak.go b/core/api/endpoint/keycloak.go index 1e292069..a5457164 100644 --- a/core/api/endpoint/keycloak.go +++ b/core/api/endpoint/keycloak.go @@ -89,7 +89,7 @@ func AuthenticateKeycloak(w http.ResponseWriter, r *http.Request) { // Decode and verify Keycloak JWT claims, err := decodeKeycloakJWT(a.Token, pk) if err != nil { - writeServerError(w, method, err) + util.WriteRequestError(w, err.Error()) return } diff --git a/core/api/util/writeHTTP.go b/core/api/util/writeHTTP.go index d0a21869..d5977653 100644 --- a/core/api/util/writeHTTP.go +++ b/core/api/util/writeHTTP.go @@ -146,7 +146,6 @@ func WriteMarshalError(w http.ResponseWriter, err error) { w.WriteHeader(http.StatusBadRequest) _, err2 := w.Write([]byte("{Error: 'JSON marshal failed'}")) log.IfErr(err2) - log.Error("Failed to JSON marshal", err) } // WriteJSON serializes data as JSON to HTTP response. @@ -165,6 +164,15 @@ func WriteJSON(w http.ResponseWriter, v interface{}) { log.IfErr(err) } +// WriteRequestError sends custom error message. +func WriteRequestError(w http.ResponseWriter, msg string) { + w.Header().Set("Content-Type", "application/json; charset=utf-8") + w.WriteHeader(http.StatusBadRequest) + + _, err := w.Write([]byte(fmt.Sprintf("{Error: '%s'}", msg))) + log.IfErr(err) +} + // WriteBadLicense writes 402 when license is invalid func WriteBadLicense(w http.ResponseWriter) { w.Header().Set("Content-Type", "application/json; charset=utf-8")