Duende Identity Server ile CIBA Authentication
**Proje Kodları için > GitHub
CIBA (Client Initiated Backchannel Authentication) Authentication, OAuth 2.0 ve OpenID Connect (OIDC) çerçevelerinde kullanılan bir kimlik doğrulama yöntemidir. Bu yöntem, kullanıcı etkileşiminin gerektirdiği senaryolarda kimlik doğrulama işlemlerinin daha güvenli ve kullanışlı bir şekilde gerçekleştirilmesini sağlar.
CIBA, özellikle bankacılık, finansal hizmetler ve sağlık hizmetleri gibi yüksek güvenlik gerektiren sektörlerde yaygın olarak kullanılır. Bu yöntem, kullanıcı deneyimini iyileştirirken aynı zamanda güvenliği artırmayı hedefler.
CIBA’nın Temel Özellikleri ve Kullanım Durumları
- Kullanıcı Tarafından Başlatılmayan İstekler: CIBA,kullanıcının aktif bir şekilde oturum açma işlemini başlatmadığı, ancak bir kimlik doğrulama talebinin gerektiği durumlarda etkilidir. Örneğin, bir finansal işlemi onaylamak için kullanıcının kimlik doğrulamasının yapılması gerektiğinde kullanılabilir.
- Kullanıcı Etkileşiminin Sınırlı Olduğu Durumlar: Kullanıcıların sınırlı veya hiç etkileşimi olmadan kimlik doğrulama yapmalarını gerektiren senaryolarda kullanılır. Örneğin, IoT cihazlarında veya akıllı ev sistemlerinde kimlik doğrulama gerektiren durumlarda.
- Güvenlik Gereksinimleri: Yüksek güvenlik gerektiren uygulamalarda kullanılır. CIBA, doğrudan kullanıcıya kimlik doğrulama taleplerinin gönderilmesi ve kullanıcının doğrulama işlemini bir güvenli cihaz üzerinden yapması esasına dayanır. Bu sayede güvenlik açıkları minimize edilir.
CIBA’nın Çalışma Prensibi:
Görsel Duende Sayfasından Alınmıştır.
Adım 1.
IdentityServer, istemcinin CIBA iş akışını başlatmak için kullandığı bir backchannel kimlik doğrulama isteği endpointi sağlar.
Adım 2.
İstemci kimlik doğrulaması ve temel istek parametresi doğrulaması gerçekleştirildikten sonra, istek yapılan kullanıcının kimliği belirlenmelidir. Bu, DI (Dependency Injection) içindeki IBackchannelAuthenticationUserValidator
servisi kullanılarak yapılır. Bu servisi uygulamanız ve DI sistemine kaydetmeniz gerekmektedir. ValidateRequestAsync
yöntemi istek parametrelerini doğrulayacak ve kullanıcının sub
(konu tanımlayıcısı) iddiasını içeren bir sonuç döndürecektir.
Adım 3.
Bir kullanıcı başarıyla tanımlandıktan sonra, bekleyen oturum açma isteğini temsil eden bir kayıt Backchannel Authentication Request Store’a oluşturulur.
Adım 4.
Daha sonra, kullanıcıya oturum açma isteği bildirilmelidir. Bu, DI içindeki IBackchannelAuthenticationUserNotificationService
servisi kullanılarak yapılır. Bu servisi uygulamanız ve DI sistemine kaydetmeniz gerekmektedir. SendLoginRequestAsync
yöntemi, uygun olan herhangi bir mekanizma ile (örneğin, e-posta, kısa mesaj, push bildirimi vb.) kullanıcıyla iletişime geçmeli ve muhtemelen kullanıcının oturum açma ve izin sürecini başlatması için talimatlar sağlamalıdır (belki bir bağlantı aracılığıyla, ancak diğer yaklaşımlar da düşünülebilir). Bu yöntem, kullanıcıya gönderilmesi gereken tüm bağlamsal bilgileri içeren bir BackchannelUserLoginRequest
iletilir (isteğin tamamlanmasında gerekli olan bu oturum açma isteği için InternalId
kimliği dahil).
Adım 5.
Daha sonra, kullanıcıya oturum açma isteği için bilgiler sunulmalıdır (örneğin, IdentityServer’da bir web sayfası aracılığıyla veya uygun olan diğer herhangi bir yolla). IBackchannelAuthenticationInteractionService
kullanılarak bir BackchannelUserLoginRequest
'i InternalId
ile erişilebilir. Kullanıcı izin verdikten ve oturum açmaya izin verdikten sonra, CompleteLoginRequestAsync
yöntemi kullanılarak sonuç (kullanıcının hangi kapsamları kabul ettiği dahil) kaydedilmelidir.
Adım 6.
Son olarak, istemci, sonucu almak için yapılan anketten sonra, talep edilen belirteçler (veya kullanıcı isteği reddetmişse veya zaman aşımına uğramışsa uygun bir hata) verilecektir.
Şimdi basit bir CIBA örnek projesi yapalım.
Resimde görüldüğü gibi .Net 8 projemizi oluşturduk. Projemize aşağıdaki paketleri ekleyelim.
<PackageReference Include="Duende.IdentityServer" Version="7.0.6" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerGen" Version="6.7.2" />
<PackageReference Include="Microsoft.AspNetCore.OpenApi" Version="8.0.8" />
<PackageReference Include="Swashbuckle.AspNetCore.SwaggerUI" Version="6.7.2" />
Şimdi “Stores” isimli klasör altına bekleyen CIBA işlemlerimizi tamamlayacağımız bir “ICibaRequestStore.cs” interface ini ve bu interface den kalıtım alan “CibaRequestStore.cs” isimli sınıfımızı aşağıdaki gibi oluşturalım. (Kod satırlarını açıklamalarla anlatmaya çalıştım.)
namespace CibaExample.Api.Stores;
public interface ICibaRequestStore
{
Task CompleteCibaRequestAsync(string id); //Bekleyen Ciba isteğini tamamlar
}
“CibaRequestStore.cs” sınıfına aşağıda biraz daha detaylı girmemiz gerekecek.
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Services;
using Duende.IdentityServer.Stores;
using Duende.IdentityServer.Stores.Serialization;
using System.Security.Claims;
namespace CibaExample.Api.Stores;
public class CibaRequestStore : DefaultGrantStore<BackChannelAuthenticationRequest>, ICibaRequestStore
{
private IBackchannelAuthenticationInteractionService _cibaInteractionService;
public CibaRequestStore(string grantType, IPersistedGrantStore store, IPersistentGrantSerializer serializer, IHandleGenerationService handleGenerationService, ILogger logger, IBackchannelAuthenticationInteractionService cibaInteractionService) : base(grantType, store, serializer, handleGenerationService, logger)
{
_cibaInteractionService = cibaInteractionService;
}
public async Task CompleteCibaRequestAsync(string id)
{
var persisted = await Store.GetAsync(id);
if (persisted == null)
{
throw new NullReferenceException("Grant not found");
}
var backChannelAuthenticationRequestData = Serializer.Deserialize<BackChannelAuthenticationRequest>(persisted.Data);
var completeRequest = new CompleteBackchannelLoginRequest(id)
{
InternalId = id,
Subject = new ClaimsPrincipal(new ClaimsIdentity(new Claim[]
{
new Claim("sub", persisted.SubjectId) // Kullanıcıyı tanımlayan subject (örneğin user_id)
})),
ScopesValuesConsented = backChannelAuthenticationRequestData.RequestedScopes
};
await _cibaInteractionService.CompleteLoginRequestAsync(completeRequest);
}
}
- Constructure Parametreleri:
grantType: Verilecek iznin türünü belirtir.
store (IPersistedGrantStore): Kimlik doğrulama süreçlerinde kullanılan kalıcı izinleri (persisted grant) yönetmek için kullanılan arayüzdür. Bu kalıcı izinler, OAuth 2.0 veya OpenID Connect protokollerinde kullanılan uzun süreli izinler, erişim token’ları, yenileme token’ları ve diğer izin türlerini içerir. Başlıca işlevleri;
- Token Saklama
- Token Güncelleme
- Token Silme
- Token Getirme
serializer: Verileri seri hale getirmek ve tekrar eski hale getirme için kullanılır.
handleGenerationService: Eşsiz kimlik(handle) oluşturmak için kullanılan hizmet.
logger: Loglama işlemleri için kullanılır (ILogger arayüzü)
cibaInteractionService (IBackchannelAuthenticationInteractionService): CIBA etkileşimlerini yönetmek için kullanılan hizmettir.
- CompleteCibaRequestAsync Metodu:
Bekleyen CIBA işlemini tamamlamak için yazdığımız custom metot.
Şimdi kullanıcı doğrulama işlemlerini gerçekleştireceğimiz “IBackchannelAuthenticationUserValidator” arayüzünü implemente eden “BackchannelAuthenticationUserValidator” sınıfını ve tüm bu işlemlerde örnek olarak kullanacağımız kullanıcıları içeren “TestUser” sınıfını oluşturalım.
using Duende.IdentityServer.Test;
using Duende.IdentityServer;
using IdentityModel;
using System.Security.Claims;
using System.Text.Json;
namespace CibaExample.Api;
public static class TestUsers
{
public static List<TestUser> Users
{
get
{
var address = new
{
street_address = "One Hacker Way",
locality = "Heidelberg",
postal_code = "69118",
country = "Germany"
};
return new List<TestUser>
{
new TestUser
{
SubjectId = "1",
Username = "alice",
Password = "alice",
Claims =
{
new Claim(JwtClaimTypes.Name, "Alice Smith"),
new Claim(JwtClaimTypes.GivenName, "Alice"),
new Claim(JwtClaimTypes.FamilyName, "Smith"),
new Claim(JwtClaimTypes.Email, "AliceSmith@email.com"),
new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
new Claim(JwtClaimTypes.WebSite, "http://alice.com"),
new Claim(JwtClaimTypes.Address, JsonSerializer.Serialize(address), IdentityServerConstants.ClaimValueTypes.Json)
}
},
new TestUser
{
SubjectId = "2",
Username = "bob",
Password = "bob",
Claims =
{
new Claim(JwtClaimTypes.Name, "Bob Smith"),
new Claim(JwtClaimTypes.GivenName, "Bob"),
new Claim(JwtClaimTypes.FamilyName, "Smith"),
new Claim(JwtClaimTypes.Email, "BobSmith@email.com"),
new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean),
new Claim(JwtClaimTypes.WebSite, "http://bob.com"),
new Claim(JwtClaimTypes.Address, JsonSerializer.Serialize(address), IdentityServerConstants.ClaimValueTypes.Json)
}
}
};
}
}
}
using System.Security.Claims;
using Duende.IdentityServer.Validation;
using IdentityModel;
namespace CibaExample.Api.Validators;
public class
using System.Security.Claims;
using Duende.IdentityServer.Validation;
using IdentityModel;
namespace CibaExample.Api.Validators;
public class BackchannelAuthenticationUserValidator : IBackchannelAuthenticationUserValidator
{
public Task<BackchannelAuthenticationUserValidationResult> ValidateRequestAsync(BackchannelAuthenticationUserValidatorContext userValidatorContext)
{
var user = TestUsers.Users.FirstOrDefault(u => u.Username == userValidatorContext?.LoginHint);
return Task.FromResult(new BackchannelAuthenticationUserValidationResult
{
Subject = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
{
new Claim("sub", user?.SubjectId),
new Claim(JwtClaimTypes.AuthenticationTime, DateTime.UtcNow.ToEpochTime().ToString()), // auth_time claim'i
new Claim(JwtClaimTypes.IdentityProvider, "local"),
})),
});
}
} : IBackchannelAuthenticationUserValidator
{
public Task<BackchannelAuthenticationUserValidationResult> ValidateRequestAsync(BackchannelAuthenticationUserValidatorContext userValidatorContext)
{
var user = TestUsers.Users.FirstOrDefault(u => u.Username == userValidatorContext?.LoginHint);
return Task.FromResult(new BackchannelAuthenticationUserValidationResult
{
Subject = new ClaimsPrincipal(new ClaimsIdentity(new List<Claim>
{
new Claim("sub", user?.SubjectId),
new Claim(JwtClaimTypes.AuthenticationTime, DateTime.UtcNow.ToEpochTime().ToString()), // auth_time claim'i
new Claim(JwtClaimTypes.IdentityProvider, "local"),
})),
});
}
}
Şimdi CIBA isteği oluşturduğumuzda kullanıcıya PushNotification, SMS veya Email ile onay talebi göndereğimiz sınıfı ekleyelim. Biz burada onay için konsola “ID” değerini yazdıracağız.
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Services;
namespace CibaExample.Api.Services;
public class BackchannelAuthenticationUserNotificationService : IBackchannelAuthenticationUserNotificationService
{
public Task SendLoginRequestAsync(BackchannelUserLoginRequest request)
{
Console.WriteLine($"**Sending login request to user with request ID {request.InternalId}");
return Task.CompletedTask;
}
}
Bekleyen CIBA işlemimizi konsola yazdırdığımız “ID” değeri yardımıyla onaylayacağımız endpointi ekleyelim.
using CibaExample.Api.Stores;
using Microsoft.AspNetCore.Mvc;
namespace CibaExample.Api.Apis;
public static class AuthMinimalApi
{
public static void UseCibaEndPoints(this IEndpointRouteBuilder routes)
{
var group = routes.MapGroup("ciba");
group.MapPost("verify-notification",VerifyAsync).WithName("verify").WithOpenApi();
}
private static async Task<IResult> VerifyAsync([FromServices] ICibaRequestStore store,string id)
{
await store.CompleteCibaRequestAsync(id);
return Results.Ok(true);
}
}
Ve son olarak; IdentityServer configurasyonlarını ve “Ciba Client” tanımlamalarımızı “Program.cs” sınıfımız içerisinde gerçekleştirelim.
using CibaExample.Api;
using CibaExample.Api.Apis;
using CibaExample.Api.Services;
using CibaExample.Api.Stores;
using CibaExample.Api.Validators;
using Duende.IdentityServer.Models;
using Duende.IdentityServer.Services;
using Duende.IdentityServer.Stores;
using Duende.IdentityServer.Stores.Serialization;
using IdentityModel;
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddIdentityServer(opt =>
{
opt.Events.RaiseErrorEvents = true;
opt.Events.RaiseSuccessEvents = true;
opt.Events.RaiseFailureEvents = true;
opt.Ciba.DefaultLifetime = 600;
})
.AddInMemoryClients(new List<Client>
{
new Client
{
ClientId = "ciba_client",
AllowedGrantTypes = { OidcConstants.GrantTypes.Ciba, OidcConstants.GrantTypes.RefreshToken},
RefreshTokenExpiration = TokenExpiration.Sliding,
AllowOfflineAccess = true,
AllowAccessTokensViaBrowser = false,
CoordinateLifetimeWithUserSession = true,
UpdateAccessTokenClaimsOnRefresh = true,
CibaLifetime = 180,
RefreshTokenUsage = TokenUsage.ReUse,
Enabled = true,
ClientSecrets = { new Secret("secret".Sha256()) },
AllowedScopes = {"openid", "profile", "api" },
AccessTokenLifetime = 180,
IdentityTokenLifetime = 180
}
})
.AddInMemoryIdentityResources(new List<IdentityResource>
{
new IdentityResources.OpenId(),
new IdentityResources.Profile()
})
.AddInMemoryApiScopes(new List<ApiScope>
{
new ApiScope("api", "Ciba Api")
}).AddTestUsers(TestUsers.Users)
.AddBackchannelAuthenticationUserValidator<BackchannelAuthenticationUserValidator>()
.AddBackchannelAuthenticationUserNotificationService<BackchannelAuthenticationUserNotificationService>()
.AddDeveloperSigningCredential();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddTransient<ICibaRequestStore, CibaRequestStore>(serviceProvider =>
{
var store = serviceProvider.GetRequiredService<IPersistedGrantStore>();
var serializer = serviceProvider.GetRequiredService<IPersistentGrantSerializer>();
var handleGenerationService = serviceProvider.GetRequiredService<IHandleGenerationService>();
var logger = serviceProvider.GetRequiredService<ILogger<CibaRequestStore>>();
var cibaInteractionService = serviceProvider.GetRequiredService<IBackchannelAuthenticationInteractionService>();
return new CibaRequestStore(OidcConstants.GrantTypes.Ciba, store, serializer, handleGenerationService, logger, cibaInteractionService);
});
var app = builder.Build();
// Configure the HTTP request pipeline.
app.UseSwagger();
app.UseSwaggerUI();
app.UseIdentityServer();
app.UseCibaEndPoints();
app.Run();
Bu konfigürasyonları açıklamak gerekirse;
builder.Services.AddIdentityServer(opt =>
{
opt.Events.RaiseErrorEvents = true;
opt.Events.RaiseSuccessEvents = true;
opt.Events.RaiseFailureEvents = true;
opt.Ciba.DefaultLifetime = 600;
})
IdentityServer’ı uygulamaya ekledik ve “opt.Events.Raise*” ile hata, uyarı ve başarılı işlemleri izlememizi sağlayan ayarları tanımladık. “opt.Ciba.DefaultLifetime” ile de CIBA işlemlerimizin ömrünü tanımladık (saniye cinsinden).
.AddInMemoryClients(new List<Client>
{
new Client
{
ClientId = "ciba_client",//İstemcinin benzersiz kimlik bilgisi.
AllowedGrantTypes = { OidcConstants.GrantTypes.Ciba, OidcConstants.GrantTypes.RefreshToken},//İstemcinin kullanabileceği izin türleri (CIBA ve Refresh Token'ı).
RefreshTokenExpiration = TokenExpiration.Sliding,//Refresh token'ının süresi, sürekli yenilenir
AllowOfflineAccess = true,//İstemci çevrimdışı erişim için yenileme token'ı alabilir.
AllowAccessTokensViaBrowser = false,//Erişim token'ı tarayıcı üzerinden alınamaz.
CoordinateLifetimeWithUserSession = true,//Token ömrü, kullanıcı oturumu ile koordine edilir.
UpdateAccessTokenClaimsOnRefresh = true, //Yenileme token'ı kullanıldığında erişim token'ı talep edilir.
CibaLifetime = 180,//Ciba token'ının süresi
RefreshTokenUsage = TokenUsage.ReUse,//Refresh token'ın kullanımı, tekrar kullanılabilir.
Enabled = true,//İstemci etkin mi?
ClientSecrets = { new Secret("secret".Sha256()) },//İstemci için paylaşılan secret.
AllowedScopes = {"openid", "profile", "api" },//İstemcinin erişebileceği kapsamlar.
AccessTokenLifetime = 180,//Erişim token'ının süresi
IdentityTokenLifetime = 180//Kimlik token'ının süresi
}
})
Yukarıda “Client” tanımlamalarımızı yaptık.
.AddInMemoryIdentityResources(new List<IdentityResource>
{
new IdentityResources.OpenId(), //openid kapsamı
new IdentityResources.Profile() //profile kapsamı
})
.AddInMemoryApiScopes(new List<ApiScope>
{
new ApiScope("api", "Ciba Api") //Api kapsamı
}).AddTestUsers(TestUsers.Users)
.AddBackchannelAuthenticationUserValidator<BackchannelAuthenticationUserValidator>() //BackchannelAuthenticationUserValidator sınıfını ekler.
.AddBackchannelAuthenticationUserNotificationService<BackchannelAuthenticationUserNotificationService>() //BackchannelAuthenticationUserNotificationService sınıfını ekler.
.AddDeveloperSigningCredential(); //Geliştirici için imza anahtarı oluşturur.
Şimdi uygulamamızı “Postman” ve “Console” üzerinden test edelim. Uygulama ayağa kalktıktan sonra;
Adresine gittiğimizde aşağıdaki konfigürasyon bilgilerini görmemiz gerekli;
Açık hali;
{
"issuer": "http://localhost:5086",
"jwks_uri": "http://localhost:5086/.well-known/openid-configuration/jwks",
"authorization_endpoint": "http://localhost:5086/connect/authorize",
"token_endpoint": "http://localhost:5086/connect/token",
"userinfo_endpoint": "http://localhost:5086/connect/userinfo",
"end_session_endpoint": "http://localhost:5086/connect/endsession",
"check_session_iframe": "http://localhost:5086/connect/checksession",
"revocation_endpoint": "http://localhost:5086/connect/revocation",
"introspection_endpoint": "http://localhost:5086/connect/introspect",
"device_authorization_endpoint": "http://localhost:5086/connect/deviceauthorization",
"backchannel_authentication_endpoint": "http://localhost:5086/connect/ciba",
"pushed_authorization_request_endpoint": "http://localhost:5086/connect/par",
"require_pushed_authorization_requests": false,
"frontchannel_logout_supported": true,
"frontchannel_logout_session_supported": true,
"backchannel_logout_supported": true,
"backchannel_logout_session_supported": true,
"scopes_supported": [
"openid",
"profile",
"api",
"offline_access"
],
"claims_supported": [
"sub",
"name",
"family_name",
"given_name",
"middle_name",
"nickname",
"preferred_username",
"profile",
"picture",
"website",
"gender",
"birthdate",
"zoneinfo",
"locale",
"updated_at"
],
"grant_types_supported": [
"authorization_code",
"client_credentials",
"refresh_token",
"implicit",
"password",
"urn:ietf:params:oauth:grant-type:device_code",
"urn:openid:params:grant-type:ciba"
],
"response_types_supported": [
"code",
"token",
"id_token",
"id_token token",
"code id_token",
"code token",
"code id_token token"
],
"response_modes_supported": [
"form_post",
"query",
"fragment"
],
"token_endpoint_auth_methods_supported": [
"client_secret_basic",
"client_secret_post"
],
"id_token_signing_alg_values_supported": [
"RS256"
],
"subject_types_supported": [
"public"
],
"code_challenge_methods_supported": [
"plain",
"S256"
],
"request_parameter_supported": true,
"request_object_signing_alg_values_supported": [
"RS256",
"RS384",
"RS512",
"PS256",
"PS384",
"PS512",
"ES256",
"ES384",
"ES512",
"HS256",
"HS384",
"HS512"
],
"prompt_values_supported": [
"none",
"login",
"consent",
"select_account"
],
"authorization_response_iss_parameter_supported": true,
"backchannel_token_delivery_modes_supported": [
"poll"
],
"backchannel_user_code_parameter_supported": true,
"dpop_signing_alg_values_supported": [
"RS256",
"RS384",
"RS512",
"PS256",
"PS384",
"PS512",
"ES256",
"ES384",
"ES512"
]
}
Şimdi Postman ile CIBA isteğimizi başlatalım.
adresine “client_id”, “client_secret”, “grant_type”, “login_hint” (biz username olarak aldık), scope bilgileri ile istek attık. Tüm bu tanımlamalarımızı Program.cs sınıfında yapmıştık.
IdentityServer bizim için birçok şeyi halletti ancak kendi özelleştirdiğimiz yerler için işlem adımlarına bakarsak;
- BackchannelAuthenticationUserValidator sınıfı ile “alice” kullanıcısını doğruladık.
- BackchannelAuthenticationUserNotificationService sınıfı ile konsola doğrulama yapacağımız “InternalId” değerini bastık.
Bu kısımda SMS, PushNotification, Email gibi kullanıcıya doğrulama gönderdiğimizi hayal edebilirsiniz
Response değerimiz;
{
"auth_req_id": "3653C1E38A355A8978FCFEA87C48450AEA93A1A9044B20A1FEB37965DB5799AA-1",
"expires_in": 180,
"interval": 5
}
Konsola bastığımız değer;
**Sending login request to user with request ID 72A5AB4E8ABBE757AF2EDB094BEC9EF29E3557C000CCD37AFA5B72688F27166F
Şimdi buradaki “request ID” değerimizi (ki aslında bu PersistedStore da saklanan CIBA isteğinin Id değeridir) “ciba/verify-notification” endpointi yardımıyla doğrulayalım.
Doğrulama isteği tamamlandıktan sonra artık ilk ciba isteğimizden dönen “auth_req_id” değeri ile “id_token”, “access_token” ve “resfresh_token” parametrelerimizi alabiliriz.
Adresine aşağıdaki gibi “Postman” üzerinden isteiğimizi atalım.
ve tokenlarımızı başarılı şekilde aldık.
{
"id_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjE1NjFDMEMxQUMzRTYzRjc1NUJGMDA0NTE5Rjg2NTBBIiwidHlwIjoiSldUIn0.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjUwODYiLCJuYmYiOjE3MjU3OTA5NzgsImlhdCI6MTcyNTc5MDk3OCwiZXhwIjoxNzI1NzkxMTU4LCJhdWQiOiJjaWJhX2NsaWVudCIsImF0X2hhc2giOiI3LU9LT25BRkQyX2NISzRzbFlLRFN3Iiwic3ViIjoiMSIsImF1dGhfdGltZSI6MTcyNTc5MDk2NiwiaWRwIjoibG9jYWwifQ.CzOzPbEqa8jN84tL13VH396Qt3IxDOUmPDtbldEC72n_cv_BuqFs1l6S_AGc3FGWA_eW5_lNPEAn-MOT83d0T1WH2CcrmyOICAkEechypZEJIfSPsSpZga911tcz5CRc7sQjEMdCiX7_1byl2ubpXS87HZt8mZkhRfUk0f2e2MfvX_aurBQtWNhXhBuL4VLTocAbPvRr09bDCEbuHuvy39jPcn36r86OjLrnVelATkjswjFnGVaDZkmsPNciDgo-Ivqst0RpvoTZvgwoDiKSQp9vVJ3qWxKfH4AJhLc9Ds8SYX9Rv66raFTosyvKodde8ZWh9DndE-oaigO-cqvmzQ",
"access_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6IjE1NjFDMEMxQUMzRTYzRjc1NUJGMDA0NTE5Rjg2NTBBIiwidHlwIjoiYXQrand0In0.eyJpc3MiOiJodHRwOi8vbG9jYWxob3N0OjUwODYiLCJuYmYiOjE3MjU3OTA5NzgsImlhdCI6MTcyNTc5MDk3OCwiZXhwIjoxNzI1NzkxMTU4LCJzY29wZSI6WyJvcGVuaWQiLCJwcm9maWxlIiwiYXBpIiwib2ZmbGluZV9hY2Nlc3MiXSwiY2xpZW50X2lkIjoiY2liYV9jbGllbnQiLCJzdWIiOiIxIiwiYXV0aF90aW1lIjoxNzI1NzkwOTY2LCJpZHAiOiJsb2NhbCIsImp0aSI6IkMwRkIzOTlCRTc3NTEwNjBDOUIxQUJBNTU3MzFCMTI5In0.LLHS72AWpI5L4943dLD9UdYs4f08BM9TgWA_5LGIAOvloenER83U2ENmG_BHxFeU3mZOZPugvtfzyEQ8iruOMN80bwOZL7wb_bSE1FoBMepAvaHxc8BWNk_O6GBSJyO-0GPjkmFGu-mCENBmRGvwPVI2ydzubQnGrvXxHQs9OWfM9OOVeM44aRpVlcFX86ZDYCjd9uhtzMkNTJ4gMPRVDAPMOqqePPFbV8ON9RdleivjQmmrFJUrDfTOdSXjQW64VOEicJwZgg0Doi-XGtjmHDVD_Cfi-8zEeH2tNqIVuTpD9EszcnPGMWo5cJT3_fQ-EoQXwRAGNa_IC2Y6Y0y-hw",
"expires_in": 180,
"token_type": "Bearer",
"refresh_token": "0CA49D2668E29984C4622D1F12786D55E657793F1FF255876EAB6005B2BFE712-1",
"scope": "openid profile api offline_access"
}
Böylelikle yazımızın sonuna geldik. Bir sonraki yazımda “.Net 8 ile Kafka Entegrasyonu” konusuna değinmeye çalışacağım.
0 Yorum