Аутентификация IdentityManager с IdentityServer в другом домене
После проверки подлинности IdentityServer IdentityManager выдает внутренний токен, подобный этому. (Пожалуйста, запустите пример и проверьте токен на предъявителя, отправленный после запроса на /api или api/Users)
authorization:Bearer UQgpIqqyn_lgUukES3PqHFEuf0_2sz26Jsh848K_4DYdiYeQLkSazg43MT2BdWSC-EY--iUYAPKk4rD9-8sq0_nbf2Z7XDzPlcDL0LdAP8oNyKUDCOLeap9zCEaB4ve1VE1Q_e5JGYsx_jTvs-yYlUI5fMn-6OBxunlNcTwPq-xv6hOXZhh-PUGIE9Ndhkptd0zt5r1A3UAvvTk72yI6yD40yRnl1KhNEQw33UNVMIeV4vWqwiXHtyoxi87e3r4_x3IyzZeEqxtwPIPH1l6o1s7HfZozspaTbaq9gPLvuaXa0dQjf5lA2CIGs5z8Fa3W
Мне действительно нужен IdentityManager, чтобы сохранить JWT, выпущенный моим собственным IdentityServer во время процесса входа в систему, и использовать этот токен для вызова API вместо использования токена вышеупомянутого типа. Зачем? Потому что я хочу вызвать внешний API из самого IdentityManager, который ожидает токен, выданный моим собственным сервером IdentityServer.
Использование HostSecurityConfiguration или LocalSecurityConfiguration не работает для меня, потому что они используют внутренне OAuthAuthorizationServerProvider, и этот провайдер выдает токен (тот, что указан выше), который недопустим для API, который в конечном итоге IdentityManager будет вызывать внутренне. Сохраненный токен ДОЛЖЕН быть выдан моим собственным IdentityServer, поскольку внешний API ожидает от него токен.
Я попытался использовать ExternalBearerTokenConfiguration без успеха. Каждый раз, когда я пытался что-то сделать с этим классом, я перенаправляюсь на https://localhost:44337/idm/connect/authorize?state=8030030589322682&nonce=7778993666881238&client_id=idmgr&response_type=id_token%20token, и этот URL-адрес, очевидно, не существует, так как полномочия запускаются с https://localhost:44337/ids и ExternalBearerTokenConfiguration предполагает, что мой провайдер находится в том же домене.
Это конфигурация для ExternalBearerTokenConfiguration
idm.UseIdentityManager(new IdentityManagerOptions
{
Factory = factory,
SecurityConfiguration = new ExternalBearerTokenConfiguration()
{
RequireSsl = false,
SigningCert = Cert.Load(),
Issuer = "https://localhost:44337/ids",
Scope = "idmgr",
Audience = $"https://localhost:44337/ids/resources",
BearerAuthenticationType = "Cookies"
}
});
Идя в другом направлении, я обнаружил, что, изменяя метод GetResponseMessage() на IdentityManager.Assets.EmbeddedHtmlResult, я могу перейти к своему IdentityServer и прошу аутентификацию, которая действительно хороша. Я могу получить свой id_token и токен доступа со всем внутри, как вы можете видеть. Хорошая мысль об этом подходе состоит в том, что токен, сохраненный внутри, является тем, который я получаю от моего IdentityServer.
{
"client_id": "idmgr_client",
"scope": [
"openid",
"idmgr",
"WebUserAccountsApi"
],
"sub": "951a965f-1f84-4360-90e4-3f6deac7b9bc",
"amr": [
"password"
],
"auth_time": 1505323819,
"idp": "idsrv",
"name": "Admin",
"role": "IdentityManagerAdministrator",
"iss": "https://localhost:44336/ids",
"aud": "https://localhost:44336/ids/resources",
"exp": 1505327419,
"nbf": 1505323819
}
Итак, теперь у меня есть почти все, что мне было нужно, когда IdentityServer отправляет меня обратно к моей конечной точке / idm (которая является конечной точкой для IdentityManager).UseIdentityServerBearerTokenAuthentication проверяет мой токен, поэтому я получил разрешение, и я уверен в этом, потому что я вижу все в эта строка в коде ниже => context.Authentication.User.Identity.IsAuthenticated. Проблема в том, что UseIdentityManager не принимает мою авторизацию, даже если UseIdentityServerBearerTokenAuthentication уже это сделал.
Когда я удаляю безопасность в проекте IdentityManager и помещаю точку останова, например, в api / Users, я вижу, что у Принципала есть некоторые значения, но все они пусты. Претензии пусты, само удостоверение имеет объект, но не аутентифицировано. Возможно, мне не хватает чего-то в этом коде, что является связующим звеном между моей аутентификацией UseIdentityServerBearerTokenAuthentication и UseIdentityManager.
app.Map("/idm", idm =>
{
var factory = new IdentityManagerServiceFactory();
var rand = new System.Random();
var users = Users.Get(rand.Next(5000, 20000));
var roles = Roles.Get(rand.Next(15));
factory.Register(new Registration<ICollection<InMemoryUser>>(users));
factory.Register(new Registration<ICollection<InMemoryRole>>(roles));
factory.IdentityManagerService = new Registration<IIdentityManagerService, InMemoryIdentityManagerService>();
idm.Use(async (context, next) =>
{
await next.Invoke();
});
JwtSecurityTokenHandler.InboundClaimTypeMap = new Dictionary<string, string>();
idm.UseIdentityServerBearerTokenAuthentication(new IdentityServerBearerTokenAuthenticationOptions
{
Authority = Constants.authority,
RequiredScopes = new[] { "idmgr" }
});
idm.Use(async (context, next) =>
{
if (context.Authentication.User != null &&
context.Authentication.User.Identity != null &&
context.Authentication.User.Identity.IsAuthenticated)
{
/*var xxx = "";
}
await next.Invoke();
});
idm.UseIdentityManager(new IdentityManagerOptions
{
Factory = factory
});
idm.Use(async (context, next) =>
{
await next.Invoke();
});
});
Если вы знаете, что мне не хватает, пожалуйста, прокомментируйте. Все идеи приветствуются. Если вы знаете, как пройти проверку подлинности в IdentityManager с помощью собственного IdentityServer, расскажите, пожалуйста, как это сделать.
Заранее спасибо. Даниил
2 ответа
Я нашел решение для этого.
Я создаю класс с именем OAuthSettings, а другой - с именем EmptySecurityConfiguration. Я использую их так:
idm.UseIdentityManager(new IdentityManagerOptions
{
Factory = factory,
SecurityConfiguration = new EmptySecurityConfiguration
{
OAuthSettings = new OAuthSettings()
{
authorization_endpoint = authority + "/connect/authorize",
client_id = "idmgr_client",
authority = authority,
response_type = "id_token token",
redirect_uri = idmUrl + "/#/callback/",
//scope = "openid",
scope = "openid idmgr MyApi",
//response_mode = ""
acr_values = "tenant:anything",
load_user_profile = true
}
}
});
Мне пришлось изменить класс SecurityConfiguration, чтобы добавить мое свойство OAuthSettings.
Тогда я использую это так внутри EmbeddedHtmlResult
OAuthSettings OAuthSettings = null;
if (this.securityConfiguration.OAuthSettings == null)
{
OAuthSettings = new OAuthSettings
{
authorization_endpoint = this.authorization_endpoint,
client_id = Constants.IdMgrClientId
};
}
else
{
OAuthSettings = this.securityConfiguration.OAuthSettings;
}
var html = AssetManager.LoadResourceString(this.file,
new {
pathBase = this.path,
model = Newtonsoft.Json.JsonConvert.SerializeObject(new
{
PathBase = this.path,
ShowLoginButton = this.securityConfiguration.ShowLoginButton,
oauthSettings = OAuthSettings
})
});
После этого вы должны запустить код и все. Вы получили токен, выданный IdentityServer, и сохранили его для использования в бите javascript.
Теперь токен на предъявителя выглядит так:
authorization:Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6ImEzck1VZ01Gdjl0UGNsTGE2eUYzekFrZnF1RSIsImtpZCI6ImEzck1VZ01Gdjl0UGNsTGE2eUYzekFrZnF1RSJ9.eyJjbGllbnRfaWQiOiJpZG1ncl9jbGllbnQiLCJzY29wZSI6WyJvcGVuaWQiLCJpZG1nciIsIk15QXBpIl0sInN1YiI6Ijk1MWE5NjVmLTFmODQtNDM2MC05MGU0LTNmNmRlYWM3YjliYyIsImFtciI6WyJwYXNzd29yZCJdLCJhdXRoX3RpbWUiOjE1MDU1NzYzNTAsImlkcCI6Imlkc3J2IiwibmFtZSI6IkFkbWluIiwicm9sZSI6IklkZW50aXR5TWFuYWdlckFkbWluaXN0cmF0b3IiLCJpc3MiOiJodHRwczovL2xvY2FsaG9zdDo0NDMzOC9pZHMiLCJhdWQiOiJodHRwczovL2xvY2FsaG9zdDo0NDMzOC9pZHMvcmVzb3VyY2VzIiwiZXhwIjoxNTA1NTgwMzU4LCJuYmYiOjE1MDU1NzY3NTh9.iVsEuswGDdMGo-x-NdPxMEln6or9e7p8G-8iSK746_Wapcwi_-N7EcY3G8GKj0YvExO4i605kfNjsTDAd14zQvT6UyU8_gcGO84DhQRM_MWpirfhlPWu6flXT4dRzYberjgHhDEOzROsrHofVAAZD_51BEE1FgAQrqCCWar2POSi9AsLFJ_AxFRnMlbZbZy8adJiMGOUFhtBXzhJVYzuolAMJ08NBTzmaK5vLsEn9Ok-09ZGX3MOpq2aBfES1hRJKEP-LDhMNo4dQn0mQ9Y-gGvkpXMmZQ6tC8yUs2PokJ5eGsFqevK6zpvJDiKPPjoN01QJtEqZ2UU_oGzMEKwyUA
Я создал репозиторий GitHub с кодом
В репозитории IdentityManager есть пример использования IdentityServer3 в качестве Idp для IdentityManager.
Некоторое уместное обсуждение может быть найдено в этой теме также...
Редактировать:
Я не изучал, что IdentityManager внутренне делает с токенами, как вы описали. Однако, для вашего внешнего вызова API, не могли бы вы также запросить токен доступа (вместо только id_token), затем сохранить этот токен доступа и использовать его для выполнения вызовов к вашему внешнему API? Это будет токен, выданный Identity Server, и по умолчанию это будет JWT.
Вот как изменится код из примера; пожалуйста, смотрите код под двумя комментариями с пометкой: "--EDIT-- ..."
По сути, я просто запрашиваю токен доступа и затем сохраняю его....
app.UseOpenIdConnectAuthentication(new Microsoft.Owin.Security.OpenIdConnect.OpenIdConnectAuthenticationOptions
{
AuthenticationType = "oidc",
Authority = "https://localhost:44337/ids",
ClientId = "idmgr_client",
RedirectUri = "https://localhost:44337",
// ---EDIT--- request id_token AND access_token
ResponseType = "id_token token",
UseTokenLifetime = false,
Scope = "openid idmgr",
SignInAsAuthenticationType = "Cookies",
Notifications = new Microsoft.Owin.Security.OpenIdConnect.OpenIdConnectAuthenticationNotifications
{
SecurityTokenValidated = n =>
{
n.AuthenticationTicket.Identity.AddClaim(new Claim("id_token", n.ProtocolMessage.IdToken));
// --EDIT-- save access_token
n.AuthenticationTicket.Identity.AddClaim(new Claim("access_token", n.ProtocolMessage.AccessToken));
return Task.FromResult(0);
},
RedirectToIdentityProvider = async n =>
{
if (n.ProtocolMessage.RequestType == Microsoft.IdentityModel.Protocols.OpenIdConnectRequestType.LogoutRequest)
{
var result = await n.OwinContext.Authentication.AuthenticateAsync("Cookies");
if (result != null)
{
var id_token = result.Identity.Claims.GetValue("id_token");
if (id_token != null)
{
n.ProtocolMessage.IdTokenHint = id_token;
n.ProtocolMessage.PostLogoutRedirectUri = "https://localhost:44337/idm";
}
}
}
}
}
});