Android 用于ionic应用程序的Key斗篷:带有cordova native的Key斗篷js不起作用
我正在尝试在我的ionic(4)cordova应用程序中使用Keyclope js(来自4.4.0.Final)库。 我已遵守了公司的指示和指示。 我已经在AppBrowser中安装了Android 用于ionic应用程序的Key斗篷:带有cordova native的Key斗篷js不起作用,android,cordova,oauth-2.0,keycloak,ionic4,Android,Cordova,Oauth 2.0,Keycloak,Ionic4,我正在尝试在我的ionic(4)cordova应用程序中使用Keyclope js(来自4.4.0.Final)库。 我已遵守了公司的指示和指示。 我已经在AppBrowser中安装了cordova插件浏览器选项卡,cordova插件深度链接,cordova插件。 在myconfig.xml中添加了 这就是我对config.xml的修改 <widget id="org.phidatalab.radar_armt"....> <plugin name="cordova-plug
cordova插件浏览器选项卡
,cordova插件深度链接
,cordova插件。
在myconfig.xml中添加了
这就是我对config.xml的修改
<widget id="org.phidatalab.radar_armt"....>
<plugin name="cordova-plugin-browsertab" spec="0.2.0" />
<plugin name="cordova-plugin-inappbrowser" spec="3.0.0" />
<plugin name="cordova-plugin-deeplinks" spec="1.1.0" />
<preference name="AndroidLaunchMode" value="singleTask" />
<allow-intent href="http://*/*" />
<allow-intent href="https://*/*" />
<universal-links>
<host name="keycloak-cordova-example.exampledomain.net" scheme="https">
<path event="keycloak" url="/login" />
</host>
</universal-links>
</widget>
我可以成功地为Android
构建和运行应用程序。但是,它不起作用。
我从adb
日志中获得(对于cordova
和cordova native
适配器)
若我尝试在浏览器上运行它,我会得到“universalLink未定义”
我真的很想得到一些帮助来让这个工作。我错过了什么?非常感谢您的任何帮助。
或者是否有一个变通方法/示例可以让Key斗篷为ionic(公共)客户机工作?我在这里发布我的解决方案,因为我浪费了很多时间让可用的插件为我的环境工作。keydape js
提供的实现已经相当过时。因此,如果你试图将其用于ionic-3应用程序,它就是不起作用
我的解决方案是使用InAppBrowser
插件(类似于keydove js
的cordova
方法)并遵循标准的Oauth2authorization\u code
过程。我研究了keydape js
的代码,并在此基础上实现了解决方案。多亏了keydape js
给你。
步骤1:安装[cordova inapp browser][1]
步骤2:示例keydape auth.service.ts
如下所示。这可能会取代keydape js,但仅适用于cordova
选项
import 'rxjs/add/operator/toPromise'
import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http'
import {Injectable} from '@angular/core'
import {JwtHelperService} from '@auth0/angular-jwt'
import {StorageService} from '../../../core/services/storage.service'
import {StorageKeys} from '../../../shared/enums/storage'
import {InAppBrowser, InAppBrowserOptions} from '@ionic-native/in-app-browser';
const uuidv4 = require('uuid/v4');
@Injectable()
export class AuthService {
URI_base: 'https://my-server-location/auth';
keycloakConfig: any;
constructor(
public http: HttpClient,
public storage: StorageService,
private jwtHelper: JwtHelperService,
private inAppBrowser: InAppBrowser,
) {
this.keycloakConfig = {
authServerUrl: 'https://my-server-location/auth/', //keycloak-url
realm: 'myrealmmName', //realm-id
clientId: 'clientId', // client-id
redirectUri: 'http://my-demo-app/callback/', //callback-url registered for client.
// This can be anything, but should be a valid URL
};
}
public keycloakLogin(login: boolean): Promise<any> {
return new Promise((resolve, reject) => {
const url = this.createLoginUrl(this.keycloakConfig, login);
const options: InAppBrowserOptions = {
zoom: 'no',
location: 'no',
clearsessioncache: 'yes',
clearcache: 'yes'
}
const browser = this.inAppBrowser.create(url, '_blank', options);
const listener = browser.on('loadstart').subscribe((event: any) => {
const callback = encodeURI(event.url);
//Check the redirect uri
if (callback.indexOf(this.keycloakConfig.redirectUri) > -1) {
listener.unsubscribe();
browser.close();
const code = this.parseUrlParamsToObject(event.url);
this.getAccessToken(this.keycloakConfig, code).then(
() => {
const token = this.storage.get(StorageKeys.OAUTH_TOKENS);
resolve(token);
},
() => reject("Count not login in to keycloak")
);
}
});
});
}
parseUrlParamsToObject(url: any) {
const hashes = url.slice(url.indexOf('?') + 1).split('&');
return hashes.reduce((params, hash) => {
const [key, val] = hash.split('=');
return Object.assign(params, {[key]: decodeURIComponent(val)})
}, {});
}
createLoginUrl(keycloakConfig: any, isLogin: boolean) {
const state = uuidv4();
const nonce = uuidv4();
const responseMode = 'query';
const responseType = 'code';
const scope = 'openid';
return this.getUrlForAction(keycloakConfig, isLogin) +
'?client_id=' + encodeURIComponent(keycloakConfig.clientId) +
'&state=' + encodeURIComponent(state) +
'&redirect_uri=' + encodeURIComponent(keycloakConfig.redirectUri) +
'&response_mode=' + encodeURIComponent(responseMode) +
'&response_type=' + encodeURIComponent(responseType) +
'&scope=' + encodeURIComponent(scope) +
'&nonce=' + encodeURIComponent(nonce);
}
getUrlForAction(keycloakConfig: any, isLogin: boolean) {
return isLogin ? this.getRealmUrl(keycloakConfig) + '/protocol/openid-connect/auth'
: this.getRealmUrl(keycloakConfig) + '/protocol/openid-connect/registrations';
}
loadUserInfo() {
return this.storage.get(StorageKeys.OAUTH_TOKENS).then( tokens => {
const url = this.getRealmUrl(this.keycloakConfig) + '/protocol/openid-connect/userinfo';
const headers = this.getAccessHeaders(tokens.access_token, 'application/json');
return this.http.get(url, {headers: headers}).toPromise();
})
}
getAccessToken(kc: any, authorizationResponse: any) {
const URI = this.getTokenUrl();
const body = this.getAccessTokenParams(authorizationResponse.code, kc.clientId, kc.redirectUri);
const headers = this.getTokenRequestHeaders();
return this.createPostRequest(URI, body, {
header: headers,
}).then((newTokens: any) => {
newTokens.iat = (new Date().getTime() / 1000) - 10; // reduce 10 sec to for delay
this.storage.set(StorageKeys.OAUTH_TOKENS, newTokens);
});
}
refresh() {
return this.storage.get(StorageKeys.OAUTH_TOKENS)
.then(tokens => {
const decoded = this.jwtHelper.decodeToken(tokens.access_token)
if (decoded.iat + tokens.expires_in < (new Date().getTime() /1000)) {
const URI = this.getTokenUrl();
const headers = this.getTokenRequestHeaders();
const body = this.getRefreshParams(tokens.refresh_token, this.keycloakConfig.clientId);
return this.createPostRequest(URI, body, {
headers: headers
})
} else {
return tokens
}
})
.then(newTokens => {
newTokens.iat = (new Date().getTime() / 1000) - 10;
return this.storage.set(StorageKeys.OAUTH_TOKENS, newTokens)
})
.catch((reason) => console.log(reason))
}
createPostRequest(uri, body, headers) {
return this.http.post(uri, body, headers).toPromise()
}
getAccessHeaders(accessToken, contentType) {
return new HttpHeaders()
.set('Authorization', 'Bearer ' + accessToken)
.set('Content-Type', contentType);
}
getRefreshParams(refreshToken, clientId) {
return new HttpParams()
.set('grant_type', 'refresh_token')
.set('refresh_token', refreshToken)
.set('client_id', encodeURIComponent(clientId))
}
getAccessTokenParams(code , clientId, redirectUrl) {
return new HttpParams()
.set('grant_type', 'authorization_code')
.set('code', code)
.set('client_id', encodeURIComponent(clientId))
.set('redirect_uri', redirectUrl);
}
getTokenUrl() {
return this.getRealmUrl(this.keycloakConfig) + '/protocol/openid-connect/token';
}
getTokenRequestHeaders() {
const headers = new HttpHeaders()
.set('Content-Type', 'application/x-www-form-urlencoded');
const clientSecret = (this.keycloakConfig.credentials || {}).secret;
if (this.keycloakConfig.clientId && clientSecret) {
headers.set('Authorization', 'Basic ' + btoa(this.keycloakConfig.clientId + ':' + clientSecret));
}
return headers;
}
getRealmUrl(kc: any) {
if (kc && kc.authServerUrl) {
if (kc.authServerUrl.charAt(kc.authServerUrl.length - 1) == '/') {
return kc.authServerUrl + 'realms/' + encodeURIComponent(kc.realm);
} else {
return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm);
}
} else {
return undefined;
}
}
}
注意:keydeposelogin(true)将带您进入登录页面,或者keydeposelogin(false)将带您进入keydepose的注册页面
我希望这或多或少能帮助你解决这个问题。对于那些有类似问题的人,我已经写了一篇关于它的文章,它只适用于Android和IOS平台,使用KeyClope进行登录。这应该行得通
这也维护了用户会话。试试看
此API的问题在于无法使用电容器配置路由。Hi@Baptise,不幸的是,现有的带有cordova本机实现的KeyClope js无法按预期用于ionic3。所以我自己实现了这个解决方案。我会尽快发布我已经实现的,我还没有尝试过。我只在Android上运行过,谢谢。我还花了大量时间研究KeyClope JS的标准实现,但它没有正常工作,我可以想象得到。我花了一个星期。很高兴听到这个消息helps@Ketu,我将访问令牌与其他数据一起存储在我的手机localstorage上,并通过应用程序的界面执行注销。当我注销时,我清除现有令牌。此外,您也可以尝试调用注销url,如@NehaM,我尝试了上面提到的链接,但是在我的情况下,注销会间歇性地工作。感谢这个库。身份验证工作正常,但我在我的原生Android应用程序中使用Angular的HttpClient
时遇到了CORS问题。我可以通过使用@ionic native/HTTP/ngx
中的HTTP
来避免这些问题。但是,拦截器不起作用(当然)。你是怎么解决的?
12-04 19:07:35.911 32578-32578/org.phidatalab.radar_armt D/SystemWebChromeClient: ng:///AuthModule/EnrolmentPageComponent.ngfactory.js: Line 457 : ERROR
12-04 19:07:35.911 32578-32578/org.phidatalab.radar_armt I/chromium: [INFO:CONSOLE(457)] "ERROR", source: ng:///AuthModule/EnrolmentPageComponent.ngfactory.js (457)
12-04 19:07:35.918 32578-32578/org.phidatalab.radar_armt D/SystemWebChromeClient: ng:///AuthModule/EnrolmentPageComponent.ngfactory.js: Line 457 : ERROR CONTEXT
12-04 19:07:35.919 32578-32578/org.phidatalab.radar_armt I/chromium: [INFO:CONSOLE(457)] "ERROR CONTEXT", source: ng:///AuthModule/EnrolmentPageComponent.ngfactory.js (457)
import 'rxjs/add/operator/toPromise'
import {HttpClient, HttpHeaders, HttpParams} from '@angular/common/http'
import {Injectable} from '@angular/core'
import {JwtHelperService} from '@auth0/angular-jwt'
import {StorageService} from '../../../core/services/storage.service'
import {StorageKeys} from '../../../shared/enums/storage'
import {InAppBrowser, InAppBrowserOptions} from '@ionic-native/in-app-browser';
const uuidv4 = require('uuid/v4');
@Injectable()
export class AuthService {
URI_base: 'https://my-server-location/auth';
keycloakConfig: any;
constructor(
public http: HttpClient,
public storage: StorageService,
private jwtHelper: JwtHelperService,
private inAppBrowser: InAppBrowser,
) {
this.keycloakConfig = {
authServerUrl: 'https://my-server-location/auth/', //keycloak-url
realm: 'myrealmmName', //realm-id
clientId: 'clientId', // client-id
redirectUri: 'http://my-demo-app/callback/', //callback-url registered for client.
// This can be anything, but should be a valid URL
};
}
public keycloakLogin(login: boolean): Promise<any> {
return new Promise((resolve, reject) => {
const url = this.createLoginUrl(this.keycloakConfig, login);
const options: InAppBrowserOptions = {
zoom: 'no',
location: 'no',
clearsessioncache: 'yes',
clearcache: 'yes'
}
const browser = this.inAppBrowser.create(url, '_blank', options);
const listener = browser.on('loadstart').subscribe((event: any) => {
const callback = encodeURI(event.url);
//Check the redirect uri
if (callback.indexOf(this.keycloakConfig.redirectUri) > -1) {
listener.unsubscribe();
browser.close();
const code = this.parseUrlParamsToObject(event.url);
this.getAccessToken(this.keycloakConfig, code).then(
() => {
const token = this.storage.get(StorageKeys.OAUTH_TOKENS);
resolve(token);
},
() => reject("Count not login in to keycloak")
);
}
});
});
}
parseUrlParamsToObject(url: any) {
const hashes = url.slice(url.indexOf('?') + 1).split('&');
return hashes.reduce((params, hash) => {
const [key, val] = hash.split('=');
return Object.assign(params, {[key]: decodeURIComponent(val)})
}, {});
}
createLoginUrl(keycloakConfig: any, isLogin: boolean) {
const state = uuidv4();
const nonce = uuidv4();
const responseMode = 'query';
const responseType = 'code';
const scope = 'openid';
return this.getUrlForAction(keycloakConfig, isLogin) +
'?client_id=' + encodeURIComponent(keycloakConfig.clientId) +
'&state=' + encodeURIComponent(state) +
'&redirect_uri=' + encodeURIComponent(keycloakConfig.redirectUri) +
'&response_mode=' + encodeURIComponent(responseMode) +
'&response_type=' + encodeURIComponent(responseType) +
'&scope=' + encodeURIComponent(scope) +
'&nonce=' + encodeURIComponent(nonce);
}
getUrlForAction(keycloakConfig: any, isLogin: boolean) {
return isLogin ? this.getRealmUrl(keycloakConfig) + '/protocol/openid-connect/auth'
: this.getRealmUrl(keycloakConfig) + '/protocol/openid-connect/registrations';
}
loadUserInfo() {
return this.storage.get(StorageKeys.OAUTH_TOKENS).then( tokens => {
const url = this.getRealmUrl(this.keycloakConfig) + '/protocol/openid-connect/userinfo';
const headers = this.getAccessHeaders(tokens.access_token, 'application/json');
return this.http.get(url, {headers: headers}).toPromise();
})
}
getAccessToken(kc: any, authorizationResponse: any) {
const URI = this.getTokenUrl();
const body = this.getAccessTokenParams(authorizationResponse.code, kc.clientId, kc.redirectUri);
const headers = this.getTokenRequestHeaders();
return this.createPostRequest(URI, body, {
header: headers,
}).then((newTokens: any) => {
newTokens.iat = (new Date().getTime() / 1000) - 10; // reduce 10 sec to for delay
this.storage.set(StorageKeys.OAUTH_TOKENS, newTokens);
});
}
refresh() {
return this.storage.get(StorageKeys.OAUTH_TOKENS)
.then(tokens => {
const decoded = this.jwtHelper.decodeToken(tokens.access_token)
if (decoded.iat + tokens.expires_in < (new Date().getTime() /1000)) {
const URI = this.getTokenUrl();
const headers = this.getTokenRequestHeaders();
const body = this.getRefreshParams(tokens.refresh_token, this.keycloakConfig.clientId);
return this.createPostRequest(URI, body, {
headers: headers
})
} else {
return tokens
}
})
.then(newTokens => {
newTokens.iat = (new Date().getTime() / 1000) - 10;
return this.storage.set(StorageKeys.OAUTH_TOKENS, newTokens)
})
.catch((reason) => console.log(reason))
}
createPostRequest(uri, body, headers) {
return this.http.post(uri, body, headers).toPromise()
}
getAccessHeaders(accessToken, contentType) {
return new HttpHeaders()
.set('Authorization', 'Bearer ' + accessToken)
.set('Content-Type', contentType);
}
getRefreshParams(refreshToken, clientId) {
return new HttpParams()
.set('grant_type', 'refresh_token')
.set('refresh_token', refreshToken)
.set('client_id', encodeURIComponent(clientId))
}
getAccessTokenParams(code , clientId, redirectUrl) {
return new HttpParams()
.set('grant_type', 'authorization_code')
.set('code', code)
.set('client_id', encodeURIComponent(clientId))
.set('redirect_uri', redirectUrl);
}
getTokenUrl() {
return this.getRealmUrl(this.keycloakConfig) + '/protocol/openid-connect/token';
}
getTokenRequestHeaders() {
const headers = new HttpHeaders()
.set('Content-Type', 'application/x-www-form-urlencoded');
const clientSecret = (this.keycloakConfig.credentials || {}).secret;
if (this.keycloakConfig.clientId && clientSecret) {
headers.set('Authorization', 'Basic ' + btoa(this.keycloakConfig.clientId + ':' + clientSecret));
}
return headers;
}
getRealmUrl(kc: any) {
if (kc && kc.authServerUrl) {
if (kc.authServerUrl.charAt(kc.authServerUrl.length - 1) == '/') {
return kc.authServerUrl + 'realms/' + encodeURIComponent(kc.realm);
} else {
return kc.authServerUrl + '/realms/' + encodeURIComponent(kc.realm);
}
} else {
return undefined;
}
}
}
@Component({
selector: 'page-enrolment',
templateUrl: 'enrolment-page.component.html'
})
export class EnrolmentPageComponent {
constructor(
public storage: StorageService,
private authService: AuthService,
) {}
goToRegistration() {
this.loading = true;
this.authService.keycloakLogin(false)
.then(() => {
return this.authService.retrieveUserInformation(this.language)
});
}
}