oidc-client 从本地主机或其他主机配置发现文档 URL
oidc-client to configure discovery documentation from the local host or other URL
在 angular 8 应用程序中使用 OIDC-Client 登录之前,他们是否有任何方法从本地主机配置发现文档。
我有这个管理器,它是 OIDC 客户端的助手调用
export class AuthenticationService {
@Output() initialized: boolean = false;
static USER_LOADED_EVENT = "USER_LOADED";
static USER_UNLOADED_EVENT = "USER_UNLOADED";
//static USER_SIGNED_OUT_EVENT = "USER_SIGNED_OUT";
//static USER_EXPIRED_EVENT = "USER_EXPIRED";
static USER_RESET_EVENT = "USER_RESET";
private manager: UserManager;
private user: User = null;
private accessToken: Object = null;
private signingOut: boolean = false;
private listeners: Object;
private eventsSubject: Subject<any>;
private events: Observable<any>;
public settings: UserManagerSettings;
constructor(
private $log: Logger,
private tokenHelper: TokenHelperService,
private ngZone: NgZone, private oauthService: OAuthService) {
//Hook up some event notifications
this.listeners = {};
this.eventsSubject = new Subject<any>();
this.events = from(this.eventsSubject);
this.events.subscribe(
({ name, args }) => {
if (this.listeners[name]) {
for (let listener of this.listeners[name]) {
listener(...args);
}
}
});
}
async serviceIsReady(): Promise<void> {
await new Promise((resolve, reject) => {
const source = timer(0, 100).subscribe(t => {
if (this.initialized) {
source.unsubscribe();
resolve(true);
}
else if (t > 5000) {
source.unsubscribe();
reject(false);
}
}, error => {
reject(error);
});
});
}
/**
* Initializes the OIDC Client ready for use by the application.
*/
async initialize(openIdSettings: IOpenIdOptions): Promise<void> {
if (this.initialized) return;
this.ngZone.runOutsideAngular(() => {
this.settings = this.getClientSettings(openIdSettings);
this.manager = new UserManager(this.settings);
//Persist settings for easy access by the silent-renew iframe
window["oidc"] = {
settings: this.settings
};
});
var self = this;
this.manager.events.addAccessTokenExpiring(() => {
this.$log.info("IdSvr token expiring", new Date());
});
this.manager.events.addAccessTokenExpired(() => {
this.$log.info("IdSvr token expired", new Date());
this.logout(false);
//this.broadcast(AuthenticationService.USER_EXPIRED_EVENT);
this.broadcast(AuthenticationService.USER_RESET_EVENT);
});
this.manager.events.addSilentRenewError(e => {
this.$log.warn("IdSvr silent renew error", e.message, new Date());
this.logout(false);
});
this.manager.events.addUserLoaded(user => {
this.$log.info("IdSvr user session is ready", new Date());
this.accessToken = self.tokenHelper.getPayloadFromToken(user.access_token, false);
this.user = user;
this.broadcast(AuthenticationService.USER_LOADED_EVENT, user);
});
this.manager.events.addUserUnloaded(() => {
this.$log.info("IdSvr user session has ended", new Date());
this.broadcast(AuthenticationService.USER_UNLOADED_EVENT);
if (!this.signingOut) {
this.startAuthentication(window.location.pathname + window.location.search);
}
});
this.manager.events.addUserSignedOut(() => {
this.$log.info("IdSvr user signed out", new Date());
this.logout(false);
//this.broadcast(AuthenticationService.USER_SIGNED_OUT_EVENT);
this.broadcast(AuthenticationService.USER_RESET_EVENT);
});
this.user = await this.manager.getUser();
this.initialized = true;
}
/**
* Gets the Authorization header, to be added to any outgoing requests, that needs to be authenticated.
*/
getAuthorizationHeaders(): HttpHeaders {
return new HttpHeaders({ 'Authorization': this.getAuthorizationHeaderValue() });
}
/**
* Checks to see if a user is currently logged on.
*/
isLoggedIn(): boolean {
return this.user != null && !this.user.expired;
}
/**
* Gets all the claims assigned to the current logged on user.
*/
getProfile(): any {
return this.user.profile;
}
/**
* Gets all the claims assigned to the current logged on user.
*/
getAccessToken(): any {
return this.accessToken || this.tokenHelper.getPayloadFromToken(this.user.access_token, false);;
}
/**
* Checks to see if the current logged on user has the specified claim
* @param claimType The type of the claim the user must be assigned
* @param value The value of the claim, uses the wildcard "*", if no value provided.
*/
hasClaim(claimType: string, value?: string): boolean {
var upperValue = value === undefined || value === null
? "*"
: value.toUpperCase();
if (this.isLoggedIn()) {
const claims = this.getAccessToken()[claimType];
if (!claims)
return false;
if (typeof claims === "string")
return claims.toUpperCase() === upperValue;
else if (Object.prototype.toString.call(claims) === "[object Array]")
if (claims.filter((c) => {
return c.toUpperCase() === upperValue;
})
.length >
0)
return true;
}
return false;
}
/**
* Checks to see if the current logged on user has any of the specified claims
* @param claimTypes The type of the claim
* @param value The value of the claim, uses the wildcard "*", if no value provided.
*/
hasAnyClaim(claimTypes: string[], value?: string) {
if (this.isLoggedIn())
return false;
for (let i = 0; i < claimTypes.length; i++) {
if (this.hasClaim(claimTypes[i], value))
return true;
}
return false;
}
/**
* Gets the access token of the current logged on user.
*/
getAuthorizationHeaderValue(): string {
return `${this.user.token_type} ${this.user.access_token}`;
}
/**
* Initiates the logon process, to authenticate the user using Identity Server.
* @param returnUrl The route to load, post authentication.
*/
async startAuthentication(returnUrl: string): Promise<void> {
await this.manager.clearStaleState();
await this.manager.signinRedirect({
data: {
returnUrl: returnUrl
}
}).catch(err => {
this.$log.debug("IdSvr sign in failed", err);
return err;
});
}
/**
* Processes the callback from Identity Server, post authentication.
*/
async completeAuthentication(): Promise<Oidc.User> {
let user = await new Promise<Oidc.User>((resolve, reject) => {
this.ngZone.runOutsideAngular(() => {
this.manager.signinRedirectCallback().then(user => {
resolve(user);
}).catch(error => {
reject(error);
});
});
});
this.$log.debug("IdSvr user signed in");
this.user = user;
return user;
}
// private delay(ms: number): Promise<void> {
// return new Promise<void>(resolve =>
// setTimeout(resolve, ms));
// }
/**
* Logs out the current logged in user.
*/
logout(signoutRedirect?: boolean) {
if (signoutRedirect === undefined || signoutRedirect !== false) {
this.signingOut = true;
signoutRedirect = true;
}
this.manager.stopSilentRenew();
this.manager.removeUser().then(() => {
this.manager.clearStaleState();
this.$log.debug("user removed");
if (signoutRedirect) {
this.manager.signoutRedirect();
}
}).catch(err => {
this.$log.error(err);
});
}
/**
* Gets the current logged in user.
*/
async getUser(): Promise<Oidc.User> {
return await this.manager.getUser();
}
/**
* Gets the Identity Server settings for this client application.
*/
getClientSettings(configuration: IOpenIdOptions): UserManagerSettings {
return {
authority: configuration.authority + '/',
client_id: configuration.clientId,
redirect_uri: configuration.redirectUri,
post_logout_redirect_uri: configuration.redirectUri,
response_type: configuration.responseType, // "id_token token",
scope: "openid profile email " + configuration.apiResourceId,
filterProtocolClaims: true,
loadUserInfo: true,
automaticSilentRenew: true,
monitorSession: true,
silent_redirect_uri: configuration.silentRedirectUri,
accessTokenExpiringNotificationTime: 20, //default 60
checkSessionInterval: 5000, //default 2000
silentRequestTimeout: 20000//default: 10000
};
}
on(name, listener) {
if (!this.listeners[name]) {
this.listeners[name] = [];
}
this.listeners[name].push(listener);
}
broadcast(name, ...args) {
this.eventsSubject.next({
name,
args
});
}
}
export function authenticationServiceFactory(authService: AuthenticationService, appSettings: AppSettingsService) {
return async () => {
await appSettings.serviceIsReady();
await authService.initialize(appSettings.getOpenIdOptions());
}
};
所有配置设置都在 getClientSettings
方法中。
由于某些安全问题,我无法从 okta 读取发现文档
Access to XMLHttpRequest at 'https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/.well-known/openid-configuration' from origin 'https://localhost:44307' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
相关问题link
我正在寻找一种从其他位置配置发现文档的方法。这样就不会出现 CORS 问题。有没有办法在OIDC-Client库中配置发现文档
对 https://github.com/IdentityModel/oidc-client-js 进行了一些研究,但尚未找到配置设置
尝试过此配置,但似乎不起作用
getClientSettings(configuration: IOpenIdOptions): UserManagerSettings {
return {
authority: configuration.authority + '/',
client_id: configuration.clientId,
redirect_uri: configuration.redirectUri,
post_logout_redirect_uri: configuration.redirectUri,
response_type: configuration.responseType, // "id_token token",
scope: "openid profile email " + configuration.apiResourceId,
filterProtocolClaims: true,
loadUserInfo: true,
automaticSilentRenew: true,
monitorSession: true,
silent_redirect_uri: configuration.silentRedirectUri,
accessTokenExpiringNotificationTime: 20, //default 60
checkSessionInterval: 5000, //default 2000
silentRequestTimeout: 20000,//default: 10000
metadata: {
issuer: 'https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357',
jwks_uri: 'https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/keys',
end_session_endpoint: 'https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/logout',
authorization_endpoint: 'https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/authorize'
}, signingKeys: ["HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "ES256", "ES384", "ES512"]
};
}
参考
https://github.com/IdentityModel/oidc-client-js/issues/275
https://github.com/OHIF/Viewers/issues/616
这是我从发行人那里得到的发现文档
https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/.well-known/openid-configuration
{
"issuer": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357",
"authorization_endpoint": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/authorize",
"token_endpoint": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/token",
"userinfo_endpoint": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/userinfo",
"registration_endpoint": "https://dev-166545.okta.com/oauth2/v1/clients",
"jwks_uri": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/keys",
"response_types_supported": ["code", "id_token", "code id_token", "code token", "id_token token", "code id_token token"],
"response_modes_supported": ["query", "fragment", "form_post", "okta_post_message"],
"grant_types_supported": ["authorization_code", "implicit", "refresh_token", "password"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["RS256"],
"scopes_supported": ["monash-identity-api", "openid", "profile", "email", "address", "phone", "offline_access"],
"token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post", "client_secret_jwt", "private_key_jwt", "none"],
"claims_supported": ["iss", "ver", "sub", "aud", "iat", "exp", "jti", "auth_time", "amr", "idp", "nonce", "name", "nickname", "preferred_username", "given_name", "middle_name", "family_name", "email", "email_verified", "profile", "zoneinfo", "locale", "address", "phone_number", "picture", "website", "gender", "birthdate", "updated_at", "at_hash", "c_hash"],
"code_challenge_methods_supported": ["S256"],
"introspection_endpoint": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/introspect",
"introspection_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post", "client_secret_jwt", "private_key_jwt", "none"],
"revocation_endpoint": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/revoke",
"revocation_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post", "client_secret_jwt", "private_key_jwt", "none"],
"end_session_endpoint": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/logout",
"request_parameter_supported": true,
"request_object_signing_alg_values_supported": ["HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "ES256", "ES384", "ES512"]
}
如果 CORS 被阻止,那么您需要运行执行以下步骤。
虽然这不是正确的解决方案 - 您应该让您的老板和 IT 团队同意以标准方式为 SPA 配置 Okta。
- 配置没有权限的OIDC客户端url
- 使用显式颁发者 + 授权端点配置 OIDC 客户端
- 将 OIDC 客户端配置为不获取用户信息
- 将令牌签名密钥作为数据提供给 OIDC 客户端
- 使用隐式流,with response_type=token id_token
这是我在 Azure AD 不允许来自 SPA 的 CORS 请求时使用的配置:
// OIDC Settings that work when there is no CORS support
const settings = {
// OIDC client seems to require at least a dummy value for this
authority: 'x',
// Supply these details explicitly
metadata: {
issuer: 'https://sts.windows.net/7f071fbc-8bf2-4e61-bb48-dabd8e2f5b5a/',
authorization_endpoint: 'https://login.microsoftonline.com/7f071fbc-8bf2-4e61-bb48-dabd8e2f5b5a/oauth2/authorize',
},
// When CORS is disabled, token signing keys cannot be retrieved
// The keys must be retrieved first by double hopping from the UI to API to Auth Server
signingKeys: tokenSigningKeys,
// Turn off calls to user info since CORS will block it
loadUserInfo: false,
// The URL where the Web UI receives the login result
redirect_uri: 'https://web.mycompany.com/spa/',
// The no longer recommended implicit flow must be used if CORS is disabled
response_type: 'token id_token',
// Other OAuth settings
client_id: '0ed1c9d0-68e7-4acc-abd1-a0efab2643c8',
scope: 'openid email profile',
} as UserManagerSettings;
this._userManager = new UserManager(settings);
要获取令牌签名密钥,UI 需要通过您的 API 双跳到 JWKS 端点。请注意,JWKS 密钥是 public 信息,获取它们不需要保护 - this is the JWKS Endpoint 对于我的开发人员 Azure 帐户。
在 angular 8 应用程序中使用 OIDC-Client 登录之前,他们是否有任何方法从本地主机配置发现文档。
我有这个管理器,它是 OIDC 客户端的助手调用
export class AuthenticationService {
@Output() initialized: boolean = false;
static USER_LOADED_EVENT = "USER_LOADED";
static USER_UNLOADED_EVENT = "USER_UNLOADED";
//static USER_SIGNED_OUT_EVENT = "USER_SIGNED_OUT";
//static USER_EXPIRED_EVENT = "USER_EXPIRED";
static USER_RESET_EVENT = "USER_RESET";
private manager: UserManager;
private user: User = null;
private accessToken: Object = null;
private signingOut: boolean = false;
private listeners: Object;
private eventsSubject: Subject<any>;
private events: Observable<any>;
public settings: UserManagerSettings;
constructor(
private $log: Logger,
private tokenHelper: TokenHelperService,
private ngZone: NgZone, private oauthService: OAuthService) {
//Hook up some event notifications
this.listeners = {};
this.eventsSubject = new Subject<any>();
this.events = from(this.eventsSubject);
this.events.subscribe(
({ name, args }) => {
if (this.listeners[name]) {
for (let listener of this.listeners[name]) {
listener(...args);
}
}
});
}
async serviceIsReady(): Promise<void> {
await new Promise((resolve, reject) => {
const source = timer(0, 100).subscribe(t => {
if (this.initialized) {
source.unsubscribe();
resolve(true);
}
else if (t > 5000) {
source.unsubscribe();
reject(false);
}
}, error => {
reject(error);
});
});
}
/**
* Initializes the OIDC Client ready for use by the application.
*/
async initialize(openIdSettings: IOpenIdOptions): Promise<void> {
if (this.initialized) return;
this.ngZone.runOutsideAngular(() => {
this.settings = this.getClientSettings(openIdSettings);
this.manager = new UserManager(this.settings);
//Persist settings for easy access by the silent-renew iframe
window["oidc"] = {
settings: this.settings
};
});
var self = this;
this.manager.events.addAccessTokenExpiring(() => {
this.$log.info("IdSvr token expiring", new Date());
});
this.manager.events.addAccessTokenExpired(() => {
this.$log.info("IdSvr token expired", new Date());
this.logout(false);
//this.broadcast(AuthenticationService.USER_EXPIRED_EVENT);
this.broadcast(AuthenticationService.USER_RESET_EVENT);
});
this.manager.events.addSilentRenewError(e => {
this.$log.warn("IdSvr silent renew error", e.message, new Date());
this.logout(false);
});
this.manager.events.addUserLoaded(user => {
this.$log.info("IdSvr user session is ready", new Date());
this.accessToken = self.tokenHelper.getPayloadFromToken(user.access_token, false);
this.user = user;
this.broadcast(AuthenticationService.USER_LOADED_EVENT, user);
});
this.manager.events.addUserUnloaded(() => {
this.$log.info("IdSvr user session has ended", new Date());
this.broadcast(AuthenticationService.USER_UNLOADED_EVENT);
if (!this.signingOut) {
this.startAuthentication(window.location.pathname + window.location.search);
}
});
this.manager.events.addUserSignedOut(() => {
this.$log.info("IdSvr user signed out", new Date());
this.logout(false);
//this.broadcast(AuthenticationService.USER_SIGNED_OUT_EVENT);
this.broadcast(AuthenticationService.USER_RESET_EVENT);
});
this.user = await this.manager.getUser();
this.initialized = true;
}
/**
* Gets the Authorization header, to be added to any outgoing requests, that needs to be authenticated.
*/
getAuthorizationHeaders(): HttpHeaders {
return new HttpHeaders({ 'Authorization': this.getAuthorizationHeaderValue() });
}
/**
* Checks to see if a user is currently logged on.
*/
isLoggedIn(): boolean {
return this.user != null && !this.user.expired;
}
/**
* Gets all the claims assigned to the current logged on user.
*/
getProfile(): any {
return this.user.profile;
}
/**
* Gets all the claims assigned to the current logged on user.
*/
getAccessToken(): any {
return this.accessToken || this.tokenHelper.getPayloadFromToken(this.user.access_token, false);;
}
/**
* Checks to see if the current logged on user has the specified claim
* @param claimType The type of the claim the user must be assigned
* @param value The value of the claim, uses the wildcard "*", if no value provided.
*/
hasClaim(claimType: string, value?: string): boolean {
var upperValue = value === undefined || value === null
? "*"
: value.toUpperCase();
if (this.isLoggedIn()) {
const claims = this.getAccessToken()[claimType];
if (!claims)
return false;
if (typeof claims === "string")
return claims.toUpperCase() === upperValue;
else if (Object.prototype.toString.call(claims) === "[object Array]")
if (claims.filter((c) => {
return c.toUpperCase() === upperValue;
})
.length >
0)
return true;
}
return false;
}
/**
* Checks to see if the current logged on user has any of the specified claims
* @param claimTypes The type of the claim
* @param value The value of the claim, uses the wildcard "*", if no value provided.
*/
hasAnyClaim(claimTypes: string[], value?: string) {
if (this.isLoggedIn())
return false;
for (let i = 0; i < claimTypes.length; i++) {
if (this.hasClaim(claimTypes[i], value))
return true;
}
return false;
}
/**
* Gets the access token of the current logged on user.
*/
getAuthorizationHeaderValue(): string {
return `${this.user.token_type} ${this.user.access_token}`;
}
/**
* Initiates the logon process, to authenticate the user using Identity Server.
* @param returnUrl The route to load, post authentication.
*/
async startAuthentication(returnUrl: string): Promise<void> {
await this.manager.clearStaleState();
await this.manager.signinRedirect({
data: {
returnUrl: returnUrl
}
}).catch(err => {
this.$log.debug("IdSvr sign in failed", err);
return err;
});
}
/**
* Processes the callback from Identity Server, post authentication.
*/
async completeAuthentication(): Promise<Oidc.User> {
let user = await new Promise<Oidc.User>((resolve, reject) => {
this.ngZone.runOutsideAngular(() => {
this.manager.signinRedirectCallback().then(user => {
resolve(user);
}).catch(error => {
reject(error);
});
});
});
this.$log.debug("IdSvr user signed in");
this.user = user;
return user;
}
// private delay(ms: number): Promise<void> {
// return new Promise<void>(resolve =>
// setTimeout(resolve, ms));
// }
/**
* Logs out the current logged in user.
*/
logout(signoutRedirect?: boolean) {
if (signoutRedirect === undefined || signoutRedirect !== false) {
this.signingOut = true;
signoutRedirect = true;
}
this.manager.stopSilentRenew();
this.manager.removeUser().then(() => {
this.manager.clearStaleState();
this.$log.debug("user removed");
if (signoutRedirect) {
this.manager.signoutRedirect();
}
}).catch(err => {
this.$log.error(err);
});
}
/**
* Gets the current logged in user.
*/
async getUser(): Promise<Oidc.User> {
return await this.manager.getUser();
}
/**
* Gets the Identity Server settings for this client application.
*/
getClientSettings(configuration: IOpenIdOptions): UserManagerSettings {
return {
authority: configuration.authority + '/',
client_id: configuration.clientId,
redirect_uri: configuration.redirectUri,
post_logout_redirect_uri: configuration.redirectUri,
response_type: configuration.responseType, // "id_token token",
scope: "openid profile email " + configuration.apiResourceId,
filterProtocolClaims: true,
loadUserInfo: true,
automaticSilentRenew: true,
monitorSession: true,
silent_redirect_uri: configuration.silentRedirectUri,
accessTokenExpiringNotificationTime: 20, //default 60
checkSessionInterval: 5000, //default 2000
silentRequestTimeout: 20000//default: 10000
};
}
on(name, listener) {
if (!this.listeners[name]) {
this.listeners[name] = [];
}
this.listeners[name].push(listener);
}
broadcast(name, ...args) {
this.eventsSubject.next({
name,
args
});
}
}
export function authenticationServiceFactory(authService: AuthenticationService, appSettings: AppSettingsService) {
return async () => {
await appSettings.serviceIsReady();
await authService.initialize(appSettings.getOpenIdOptions());
}
};
所有配置设置都在 getClientSettings
方法中。
由于某些安全问题,我无法从 okta 读取发现文档
Access to XMLHttpRequest at 'https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/.well-known/openid-configuration' from origin 'https://localhost:44307' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
相关问题link
我正在寻找一种从其他位置配置发现文档的方法。这样就不会出现 CORS 问题。有没有办法在OIDC-Client库中配置发现文档
对 https://github.com/IdentityModel/oidc-client-js 进行了一些研究,但尚未找到配置设置
尝试过此配置,但似乎不起作用
getClientSettings(configuration: IOpenIdOptions): UserManagerSettings {
return {
authority: configuration.authority + '/',
client_id: configuration.clientId,
redirect_uri: configuration.redirectUri,
post_logout_redirect_uri: configuration.redirectUri,
response_type: configuration.responseType, // "id_token token",
scope: "openid profile email " + configuration.apiResourceId,
filterProtocolClaims: true,
loadUserInfo: true,
automaticSilentRenew: true,
monitorSession: true,
silent_redirect_uri: configuration.silentRedirectUri,
accessTokenExpiringNotificationTime: 20, //default 60
checkSessionInterval: 5000, //default 2000
silentRequestTimeout: 20000,//default: 10000
metadata: {
issuer: 'https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357',
jwks_uri: 'https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/keys',
end_session_endpoint: 'https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/logout',
authorization_endpoint: 'https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/authorize'
}, signingKeys: ["HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "ES256", "ES384", "ES512"]
};
}
参考
https://github.com/IdentityModel/oidc-client-js/issues/275
https://github.com/OHIF/Viewers/issues/616
这是我从发行人那里得到的发现文档
https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/.well-known/openid-configuration
{
"issuer": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357",
"authorization_endpoint": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/authorize",
"token_endpoint": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/token",
"userinfo_endpoint": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/userinfo",
"registration_endpoint": "https://dev-166545.okta.com/oauth2/v1/clients",
"jwks_uri": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/keys",
"response_types_supported": ["code", "id_token", "code id_token", "code token", "id_token token", "code id_token token"],
"response_modes_supported": ["query", "fragment", "form_post", "okta_post_message"],
"grant_types_supported": ["authorization_code", "implicit", "refresh_token", "password"],
"subject_types_supported": ["public"],
"id_token_signing_alg_values_supported": ["RS256"],
"scopes_supported": ["monash-identity-api", "openid", "profile", "email", "address", "phone", "offline_access"],
"token_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post", "client_secret_jwt", "private_key_jwt", "none"],
"claims_supported": ["iss", "ver", "sub", "aud", "iat", "exp", "jti", "auth_time", "amr", "idp", "nonce", "name", "nickname", "preferred_username", "given_name", "middle_name", "family_name", "email", "email_verified", "profile", "zoneinfo", "locale", "address", "phone_number", "picture", "website", "gender", "birthdate", "updated_at", "at_hash", "c_hash"],
"code_challenge_methods_supported": ["S256"],
"introspection_endpoint": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/introspect",
"introspection_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post", "client_secret_jwt", "private_key_jwt", "none"],
"revocation_endpoint": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/revoke",
"revocation_endpoint_auth_methods_supported": ["client_secret_basic", "client_secret_post", "client_secret_jwt", "private_key_jwt", "none"],
"end_session_endpoint": "https://dev-166545.okta.com/oauth2/aus1igd7yewoAs4xa357/v1/logout",
"request_parameter_supported": true,
"request_object_signing_alg_values_supported": ["HS256", "HS384", "HS512", "RS256", "RS384", "RS512", "ES256", "ES384", "ES512"]
}
如果 CORS 被阻止,那么您需要运行执行以下步骤。
虽然这不是正确的解决方案 - 您应该让您的老板和 IT 团队同意以标准方式为 SPA 配置 Okta。
- 配置没有权限的OIDC客户端url
- 使用显式颁发者 + 授权端点配置 OIDC 客户端
- 将 OIDC 客户端配置为不获取用户信息
- 将令牌签名密钥作为数据提供给 OIDC 客户端
- 使用隐式流,with response_type=token id_token
这是我在 Azure AD 不允许来自 SPA 的 CORS 请求时使用的配置:
// OIDC Settings that work when there is no CORS support
const settings = {
// OIDC client seems to require at least a dummy value for this
authority: 'x',
// Supply these details explicitly
metadata: {
issuer: 'https://sts.windows.net/7f071fbc-8bf2-4e61-bb48-dabd8e2f5b5a/',
authorization_endpoint: 'https://login.microsoftonline.com/7f071fbc-8bf2-4e61-bb48-dabd8e2f5b5a/oauth2/authorize',
},
// When CORS is disabled, token signing keys cannot be retrieved
// The keys must be retrieved first by double hopping from the UI to API to Auth Server
signingKeys: tokenSigningKeys,
// Turn off calls to user info since CORS will block it
loadUserInfo: false,
// The URL where the Web UI receives the login result
redirect_uri: 'https://web.mycompany.com/spa/',
// The no longer recommended implicit flow must be used if CORS is disabled
response_type: 'token id_token',
// Other OAuth settings
client_id: '0ed1c9d0-68e7-4acc-abd1-a0efab2643c8',
scope: 'openid email profile',
} as UserManagerSettings;
this._userManager = new UserManager(settings);
要获取令牌签名密钥,UI 需要通过您的 API 双跳到 JWKS 端点。请注意,JWKS 密钥是 public 信息,获取它们不需要保护 - this is the JWKS Endpoint 对于我的开发人员 Azure 帐户。