OpenAM-将会话属性添加到OpenID Connect声明中

时间:2019-08-07 19:24:24

标签: saml openam openid-connect

我正在使用配置为具有SAML信任圈的OpenAM 13.5,以使用第三方IdP将登录联合到我们的应用程序。第三方收到的一些SAML断言被映射为会话级别属性。 SAML部分工作正常,但我需要将可以使用OpenID Connect的应用程序连接到OpenAM。我创建了一个OpenID Connect服务,并相应地配置了客户端,并且可以使用“应用程序-> OpenAM UI->第三方IDP-> OpenAM OIDC->应用程序”成功登录。

问题是我只能检索映射到数据存储的属性-会话属性(例如AuthLevel,IDP名称等)未包含在映射的声明中。

我试图编辑OIDC Claims默认脚本,该脚本的会话变量似乎包含我所需的内容,但不幸的是,会话变量始终为null。

这是正确的方法吗?为什么会话为空?我需要启用某些功能才能阅读它吗?

预先感谢您的帮助。

2 个答案:

答案 0 :(得分:1)

您无法在OIDC声明脚本中检索SSO会话属性,因为OAuth2客户端不会在令牌请求中发送SSO跟踪Cookie。

仅当您使用AM专有功能“始终在ID令牌中包含声明”时,才有可能。

答案 1 :(得分:0)

如Bernhard在评论中所述,一旦请求到达/ userinfo端点,OpenAM就无法将访问令牌与活动会话进行协调(并且该会话也不再存在)。

但是,通过激活专有的AM功能“始终在ID令牌中包含声明”来访问ID令牌内部的声明时,会话对象可用,我们可以轮询其属性!

对于将来的读者,这是我修改OIDC脚本的方式:

System.ComponentModel.Annotations.dll

我还必须将/* * The contents of this file are subject to the terms of the Common Development and * Distribution License (the License). You may not use this file except in compliance with the * License. * * You can obtain a copy of the License at legal/CDDLv1.0.txt. See the License for the * specific language governing permission and limitations under the License. * * When distributing Covered Software, include this CDDL Header Notice in each file and include * the License file at legal/CDDLv1.0.txt. If applicable, add the following below the CDDL * Header, with the fields enclosed by brackets [] replaced by your own identifying * information: "Portions copyright [year] [name of copyright owner]". * * Copyright 2014-2016 ForgeRock AS. */ import com.iplanet.sso.SSOException import com.sun.identity.idm.IdRepoException import org.forgerock.oauth2.core.UserInfoClaims /* * Defined variables: * logger - always presents, the "OAuth2Provider" debug logger instance * claims - always present, default server provided claims * session - present if the request contains the session cookie, the user's session object * identity - always present, the identity of the resource owner * scopes - always present, the requested scopes * requestedClaims - Map<String, Set<String>> * always present, not empty if the request contains a claims parameter and server has enabled * claims_parameter_supported, map of requested claims to possible values, otherwise empty, * requested claims with no requested values will have a key but no value in the map. A key with * a single value in its Set indicates this is the only value that should be returned. * Required to return a Map of claims to be added to the id_token claims * * Expected return value structure: * UserInfoClaims { * Map<String, Object> values; // The values of the claims for the user information * Map<String, List<String>> compositeScopes; // Mapping of scope name to a list of claim names. * } */ // user session not guaranteed to be present boolean sessionPresent = session != null def fromSet = { claim, attr -> if (attr != null && attr.size() == 1){ attr.iterator().next() } else if (attr != null && attr.size() > 1){ attr } else if (logger.warningEnabled()) { logger.warning("OpenAMScopeValidator.getUserInfo(): Got an empty result for claim=$claim"); } } attributeRetriever = { attribute, claim, identity, session, requested -> if (requested == null || requested.isEmpty()) { fromSet(claim, identity.getAttribute(attribute)) } else if (requested.size() == 1) { requested.iterator().next() } else { throw new RuntimeException("No selection logic for $claim defined. Values: $requested") } } sessionAttributeRetriever = { attribute, claim, identity, session, requested -> if (requested == null || requested.isEmpty()) { if (session != null) { fromSet(claim, session.getProperty(attribute)) } else { null } } else if (requested.size() == 1) { requested.iterator().next() } else { throw new RuntimeException("No selection logic for $claim defined. Values: $requested") } } // [ {claim}: {attribute retriever}, ... ] claimAttributes = [ "email": attributeRetriever.curry("mail"), "address": { claim, identity, session, requested -> [ "formatted" : attributeRetriever("postaladdress", claim, identity, session, requested) ] }, "phone_number": attributeRetriever.curry("telephonenumber"), "given_name": attributeRetriever.curry("givenname"), "zoneinfo": attributeRetriever.curry("preferredtimezone"), "family_name": attributeRetriever.curry("sn"), "locale": attributeRetriever.curry("preferredlocale"), "name": attributeRetriever.curry("cn"), "spid_uid": attributeRetriever.curry("employeeNumber"), "spid_idp": attributeRetriever.curry("idpEntityId"), "spid_gender": attributeRetriever.curry("description"), "spid_authType": sessionAttributeRetriever.curry("AuthType"), "spid_authLevel": sessionAttributeRetriever.curry("AuthLevel"), ] // {scope}: [ {claim}, ... ] scopeClaimsMap = [ "email": [ "email" ], "address": [ "address" ], "phone": [ "phone_number" ], "profile": [ "given_name", "zoneinfo", "family_name", "locale", "name" ], "spid": [ "spid_uid", "spid_idp", "spid_authType", "spid_authLevel", "spid_gender" ], ] if (logger.messageEnabled()) { scopes.findAll { s -> !("openid".equals(s) || scopeClaimsMap.containsKey(s)) }.each { s -> logger.message("OpenAMScopeValidator.getUserInfo()::Message: scope not bound to claims: $s") } } def computeClaim = { claim, requestedValues -> try { [ claim, claimAttributes.get(claim)(claim, identity, session, requestedValues) ] } catch (IdRepoException e) { if (logger.warningEnabled()) { logger.warning("OpenAMScopeValidator.getUserInfo(): Unable to retrieve attribute=$attribute", e); } } catch (SSOException e) { if (logger.warningEnabled()) { logger.warning("OpenAMScopeValidator.getUserInfo(): Unable to retrieve attribute=$attribute", e); } } } def computedClaims = scopes.findAll { s -> !"openid".equals(s) && scopeClaimsMap.containsKey(s) }.inject(claims) { map, s -> scopeClaims = scopeClaimsMap.get(s) map << scopeClaims.findAll { c -> !requestedClaims.containsKey(c) }.collectEntries([:]) { claim -> computeClaim(claim, null) } }.findAll { map -> map.value != null } << requestedClaims.collectEntries([:]) { claim, requestedValue -> computeClaim(claim, requestedValue) } def compositeScopes = scopeClaimsMap.findAll { scope -> scopes.contains(scope.key) } return new UserInfoClaims((Map)computedClaims, (Map)compositeScopes) 类添加到脚本类白名单。

感谢您的帮助!