Skip to content

Commit 347151c

Browse files
authored
Add Atlassian provider (#1037)
Resolves #839.
1 parent 74dd45a commit 347151c

10 files changed

+391
-2
lines changed

AspNet.Security.OAuth.Providers.sln

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -184,10 +184,12 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{C2CA4B38-A
184184
docs\bitbucket.md = docs\bitbucket.md
185185
docs\digitalocean.md = docs\digitalocean.md
186186
docs\discord.md = docs\discord.md
187+
docs\docusign.md = docs\docusign.md
187188
docs\dropbox.md = docs\dropbox.md
188189
docs\ebay.md = docs\ebay.md
189190
docs\eveonline.md = docs\eveonline.md
190191
docs\foursquare.md = docs\foursquare.md
192+
docs\gitcode.md = docs\gitcode.md
191193
docs\gitee.md = docs\gitee.md
192194
docs\github.md = docs\github.md
193195
docs\instagram.md = docs\instagram.md
@@ -219,8 +221,6 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{C2CA4B38-A
219221
docs\workweixin.md = docs\workweixin.md
220222
docs\xumm.md = docs\xumm.md
221223
docs\zendesk.md = docs\zendesk.md
222-
docs\docusign.md = docs\docusign.md
223-
docs\gitcode.md = docs\gitcode.md
224224
EndProjectSection
225225
EndProject
226226
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "AspNet.Security.OAuth.Basecamp", "src\AspNet.Security.OAuth.Basecamp\AspNet.Security.OAuth.Basecamp.csproj", "{42306484-B2BF-4B52-B950-E0CDFA58B02A}"
@@ -316,6 +316,8 @@ Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNet.Security.OAuth.VkId"
316316
EndProject
317317
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNet.Security.OAuth.GitCode", "src\AspNet.Security.OAuth.GitCode\AspNet.Security.OAuth.GitCode.csproj", "{668833D5-DB6A-475F-B0FD-A03462B037B8}"
318318
EndProject
319+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "AspNet.Security.OAuth.Atlassian", "src\AspNet.Security.OAuth.Atlassian\AspNet.Security.OAuth.Atlassian.csproj", "{D2110C1B-6FE1-4D9A-81ED-93FB2AC85049}"
320+
EndProject
319321
Global
320322
GlobalSection(SolutionConfigurationPlatforms) = preSolution
321323
Debug|Any CPU = Debug|Any CPU
@@ -734,6 +736,10 @@ Global
734736
{668833D5-DB6A-475F-B0FD-A03462B037B8}.Debug|Any CPU.Build.0 = Debug|Any CPU
735737
{668833D5-DB6A-475F-B0FD-A03462B037B8}.Release|Any CPU.ActiveCfg = Release|Any CPU
736738
{668833D5-DB6A-475F-B0FD-A03462B037B8}.Release|Any CPU.Build.0 = Release|Any CPU
739+
{D2110C1B-6FE1-4D9A-81ED-93FB2AC85049}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
740+
{D2110C1B-6FE1-4D9A-81ED-93FB2AC85049}.Debug|Any CPU.Build.0 = Debug|Any CPU
741+
{D2110C1B-6FE1-4D9A-81ED-93FB2AC85049}.Release|Any CPU.ActiveCfg = Release|Any CPU
742+
{D2110C1B-6FE1-4D9A-81ED-93FB2AC85049}.Release|Any CPU.Build.0 = Release|Any CPU
737743
EndGlobalSection
738744
GlobalSection(SolutionProperties) = preSolution
739745
HideSolutionNode = FALSE
@@ -848,6 +854,7 @@ Global
848854
{CD56ABE4-1CD2-4029-B556-E110A31A2CC4} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
849855
{F3E62C24-5F82-4CF5-A994-0E10D04FB495} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
850856
{668833D5-DB6A-475F-B0FD-A03462B037B8} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
857+
{D2110C1B-6FE1-4D9A-81ED-93FB2AC85049} = {C1352FD3-AE8B-43EE-B45B-F6E0B3FBAC6D}
851858
EndGlobalSection
852859
GlobalSection(ExtensibilityGlobals) = postSolution
853860
SolutionGuid = {C7B54DE2-6407-4802-AD9C-CE54BF414C8C}

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -162,6 +162,7 @@ If a provider you're looking for does not exist, consider making a PR to add one
162162
| Apple | [![NuGet](https://img.shields.io/nuget/v/AspNet.Security.OAuth.Apple?logo=nuget&label=NuGet&color=blue)](https://www.nuget.org/packages/AspNet.Security.OAuth.Apple/ "Download AspNet.Security.OAuth.Apple from NuGet.org") | [![MyGet](https://img.shields.io/myget/aspnet-contrib/vpre/AspNet.Security.OAuth.Apple?logo=nuget&label=MyGet&color=blue)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Apple "Download AspNet.Security.OAuth.Apple from MyGet.org") | [Documentation](https://developer.apple.com/documentation/signinwithapplerestapi "Apple developer documentation") |
163163
| ArcGIS | [![NuGet](https://img.shields.io/nuget/v/AspNet.Security.OAuth.ArcGIS?logo=nuget&label=NuGet&color=blue)](https://www.nuget.org/packages/AspNet.Security.OAuth.ArcGIS/ "Download AspNet.Security.OAuth.ArcGIS from NuGet.org") | [![MyGet](https://img.shields.io/myget/aspnet-contrib/vpre/AspNet.Security.OAuth.ArcGIS?logo=nuget&label=MyGet&color=blue)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.ArcGIS "Download AspNet.Security.OAuth.ArcGIS from MyGet.org") | [Documentation](https://developers.arcgis.com/documentation/core-concepts/security-and-authentication/what-is-oauth-2/ "ArcGIS developer documentation") |
164164
| Asana | [![NuGet](https://img.shields.io/nuget/v/AspNet.Security.OAuth.Asana?logo=nuget&label=NuGet&color=blue)](https://www.nuget.org/packages/AspNet.Security.OAuth.Asana/ "Download AspNet.Security.OAuth.Asana from NuGet.org") | [![MyGet](https://img.shields.io/myget/aspnet-contrib/vpre/AspNet.Security.OAuth.Asana?logo=nuget&label=MyGet&color=blue)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Asana "Download AspNet.Security.OAuth.Asana from MyGet.org") | [Documentation](https://asana.com/developers/documentation/getting-started/auth "Asana developer documentation") |
165+
| Atlassian | [![NuGet](https://img.shields.io/nuget/v/AspNet.Security.OAuth.Atlassian?logo=nuget&label=NuGet&color=blue)](https://www.nuget.org/packages/AspNet.Security.OAuth.Atlassian/ "Download AspNet.Security.OAuth.Atlassian from NuGet.org") | [![MyGet](https://img.shields.io/myget/aspnet-contrib/vpre/AspNet.Security.OAuth.Atlassian?logo=nuget&label=MyGet&color=blue)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Atlassian "Download AspNet.Security.OAuth.Atlassian from MyGet.org") | [Documentation](https://developer.atlassian.com/cloud/oauth/ "Atlassian developer documentation") |
165166
| Autodesk | [![NuGet](https://img.shields.io/nuget/v/AspNet.Security.OAuth.Autodesk?logo=nuget&label=NuGet&color=blue)](https://www.nuget.org/packages/AspNet.Security.OAuth.Autodesk/ "Download AspNet.Security.OAuth.Autodesk from NuGet.org") | [![MyGet](https://img.shields.io/myget/aspnet-contrib/vpre/AspNet.Security.OAuth.Autodesk?logo=nuget&label=MyGet&color=blue)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Autodesk "Download AspNet.Security.OAuth.Autodesk from MyGet.org") | [Documentation](https://forge.autodesk.com/en/docs/oauth/v2/developers_guide/overview/ "Autodesk developer documentation") |
166167
| Baidu | [![NuGet](https://img.shields.io/nuget/v/AspNet.Security.OAuth.Baidu?logo=nuget&label=NuGet&color=blue)](https://www.nuget.org/packages/AspNet.Security.OAuth.Baidu/ "Download AspNet.Security.OAuth.Baidu from NuGet.org") | [![MyGet](https://img.shields.io/myget/aspnet-contrib/vpre/AspNet.Security.OAuth.Baidu?logo=nuget&label=MyGet&color=blue)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Baidu "Download AspNet.Security.OAuth.Baidu from MyGet.org") | [Documentation](https://developer.baidu.com/ "Baidu developer documentation") |
167168
| Basecamp | [![NuGet](https://img.shields.io/nuget/v/AspNet.Security.OAuth.Basecamp?logo=nuget&label=NuGet&color=blue)](https://www.nuget.org/packages/AspNet.Security.OAuth.Basecamp/ "Download AspNet.Security.OAuth.Basecamp from NuGet.org") | [![MyGet](https://img.shields.io/myget/aspnet-contrib/vpre/AspNet.Security.OAuth.Basecamp?logo=nuget&label=MyGet&color=blue)](https://www.myget.org/feed/aspnet-contrib/package/nuget/AspNet.Security.OAuth.Basecamp "Download AspNet.Security.OAuth.Basecamp from MyGet.org") | [Documentation](https://github.com/basecamp/api/blob/master/sections/authentication.md "Basecamp developer documentation") |
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<PackageValidationBaselineVersion>9.1.0</PackageValidationBaselineVersion>
5+
<TargetFramework>$(DefaultNetCoreTargetFramework)</TargetFramework>
6+
</PropertyGroup>
7+
8+
<!-- TODO Remove once published to NuGet.org -->
9+
<PropertyGroup>
10+
<DisablePackageBaselineValidation>true</DisablePackageBaselineValidation>
11+
</PropertyGroup>
12+
13+
<PropertyGroup>
14+
<Description>ASP.NET Core security middleware enabling Atlassian authentication.</Description>
15+
<Authors>smnsht</Authors>
16+
<PackageTags>atlassian;aspnetcore;authentication;oauth;security</PackageTags>
17+
</PropertyGroup>
18+
19+
<ItemGroup>
20+
<FrameworkReference Include="Microsoft.AspNetCore.App" />
21+
<PackageReference Include="JetBrains.Annotations" PrivateAssets="All" />
22+
</ItemGroup>
23+
24+
</Project>
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
// Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
2+
// See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
3+
// for more information concerning the license and the contributors participating to this project.
4+
5+
namespace AspNet.Security.OAuth.Atlassian;
6+
7+
public class AtlassianAuthenticationDefaults
8+
{
9+
/// <summary>
10+
/// Default value for <see cref="AuthenticationScheme.Name"/>.
11+
/// </summary>
12+
public const string AuthenticationScheme = "Atlassian";
13+
14+
/// <summary>
15+
/// Default value for <see cref="AuthenticationScheme.DisplayName"/>.
16+
/// </summary>
17+
public static readonly string DisplayName = "Atlassian";
18+
19+
/// <summary>
20+
/// Default value for <see cref="AuthenticationSchemeOptions.ClaimsIssuer"/>.
21+
/// </summary>
22+
public static readonly string Issuer = "Atlassian";
23+
24+
/// <summary>
25+
/// Default value for <see cref="RemoteAuthenticationOptions.CallbackPath"/>.
26+
/// </summary>
27+
public static readonly string CallbackPath = "/signin-atlassian";
28+
29+
/// <summary>
30+
/// Default value for <see cref="OAuthOptions.AuthorizationEndpoint"/>.
31+
/// </summary>
32+
public static readonly string AuthorizationEndpoint = "https://auth.atlassian.com/authorize";
33+
34+
/// <summary>
35+
/// Default value for <see cref="OAuthOptions.TokenEndpoint"/>.
36+
/// </summary>
37+
public static readonly string TokenEndpoint = "https://auth.atlassian.com/oauth/token";
38+
39+
/// <summary>
40+
/// Default value for <see cref="OAuthOptions.UserInformationEndpoint"/>.
41+
/// </summary>
42+
public static readonly string UserInformationEndpoint = "https://api.atlassian.com/me";
43+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
// Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
2+
// See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
3+
// for more information concerning the license and the contributors participating to this project.
4+
5+
using Microsoft.Extensions.DependencyInjection;
6+
7+
namespace AspNet.Security.OAuth.Atlassian;
8+
9+
/// <summary>
10+
/// Extension methods to add Atlassian authentication capabilities to an HTTP application pipeline.
11+
/// </summary>
12+
public static class AtlassianAuthenticationExtensions
13+
{
14+
/// <summary>
15+
/// Adds <see cref="AtlassianAuthenticationHandler"/> to the specified
16+
/// <see cref="AuthenticationBuilder"/>, which enables Atlassian authentication capabilities.
17+
/// </summary>
18+
/// <param name="builder">The authentication builder.</param>
19+
/// <returns>A reference to this instance after the operation has completed.</returns>
20+
public static AuthenticationBuilder AddAtlassian([NotNull] this AuthenticationBuilder builder)
21+
{
22+
return builder.AddAtlassian(AtlassianAuthenticationDefaults.AuthenticationScheme, options => { });
23+
}
24+
25+
/// <summary>
26+
/// Adds <see cref="AtlassianAuthenticationHandler"/> to the specified
27+
/// <see cref="AuthenticationBuilder"/>, which enables Atlassian authentication capabilities.
28+
/// </summary>
29+
/// <param name="builder">The authentication builder.</param>
30+
/// <param name="configuration">The delegate used to configure the OpenID 2.0 options.</param>
31+
/// <returns>A reference to this instance after the operation has completed.</returns>
32+
public static AuthenticationBuilder AddAtlassian(
33+
[NotNull] this AuthenticationBuilder builder,
34+
[NotNull] Action<AtlassianAuthenticationOptions> configuration)
35+
{
36+
return builder.AddAtlassian(AtlassianAuthenticationDefaults.AuthenticationScheme, configuration);
37+
}
38+
39+
/// <summary>
40+
/// Adds <see cref="AtlassianAuthenticationHandler"/> to the specified
41+
/// <see cref="AuthenticationBuilder"/>, which enables Atlassian authentication capabilities.
42+
/// </summary>
43+
/// <param name="builder">The authentication builder.</param>
44+
/// <param name="scheme">The authentication scheme associated with this instance.</param>
45+
/// <param name="configuration">The delegate used to configure the Atlassian options.</param>
46+
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
47+
public static AuthenticationBuilder AddAtlassian(
48+
[NotNull] this AuthenticationBuilder builder,
49+
[NotNull] string scheme,
50+
[NotNull] Action<AtlassianAuthenticationOptions> configuration)
51+
{
52+
return builder.AddAtlassian(scheme, AtlassianAuthenticationDefaults.DisplayName, configuration);
53+
}
54+
55+
/// <summary>
56+
/// Adds <see cref="AtlassianAuthenticationHandler"/> to the specified
57+
/// <see cref="AuthenticationBuilder"/>, which enables Atlassian authentication capabilities.
58+
/// </summary>
59+
/// <param name="builder">The authentication builder.</param>
60+
/// <param name="scheme">The authentication scheme associated with this instance.</param>
61+
/// <param name="caption">The optional display name associated with this instance.</param>
62+
/// <param name="configuration">The delegate used to configure the Atlassian options.</param>
63+
/// <returns>The <see cref="AuthenticationBuilder"/>.</returns>
64+
public static AuthenticationBuilder AddAtlassian(
65+
[NotNull] this AuthenticationBuilder builder,
66+
[NotNull] string scheme,
67+
[CanBeNull] string caption,
68+
[NotNull] Action<AtlassianAuthenticationOptions> configuration)
69+
{
70+
return builder.AddOAuth<AtlassianAuthenticationOptions, AtlassianAuthenticationHandler>(scheme, caption, configuration);
71+
}
72+
}
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
3+
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
4+
* for more information concerning the license and the contributors participating to this project.
5+
*/
6+
7+
using System.Net.Http.Headers;
8+
using System.Security.Claims;
9+
using System.Text.Encodings.Web;
10+
using System.Text.Json;
11+
using Microsoft.Extensions.Logging;
12+
using Microsoft.Extensions.Options;
13+
14+
namespace AspNet.Security.OAuth.Atlassian;
15+
16+
public partial class AtlassianAuthenticationHandler : OAuthHandler<AtlassianAuthenticationOptions>
17+
{
18+
public AtlassianAuthenticationHandler(
19+
[NotNull] IOptionsMonitor<AtlassianAuthenticationOptions> options,
20+
[NotNull] ILoggerFactory logger,
21+
[NotNull] UrlEncoder encoder)
22+
: base(options, logger, encoder)
23+
{
24+
}
25+
26+
protected override async Task<AuthenticationTicket> CreateTicketAsync(
27+
[NotNull] ClaimsIdentity identity,
28+
[NotNull] AuthenticationProperties properties,
29+
[NotNull] OAuthTokenResponse tokens)
30+
{
31+
using var request = new HttpRequestMessage(HttpMethod.Get, Options.UserInformationEndpoint);
32+
request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
33+
request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", tokens.AccessToken);
34+
35+
using var response = await Backchannel.SendAsync(request, HttpCompletionOption.ResponseHeadersRead, Context.RequestAborted);
36+
if (!response.IsSuccessStatusCode)
37+
{
38+
await Log.UserProfileErrorAsync(Logger, response, Context.RequestAborted);
39+
throw new HttpRequestException("An error occurred while retrieving the user profile.");
40+
}
41+
42+
using var payload = JsonDocument.Parse(await response.Content.ReadAsStringAsync(Context.RequestAborted));
43+
44+
var principal = new ClaimsPrincipal(identity);
45+
var context = new OAuthCreatingTicketContext(principal, properties, Context, Scheme, Options, Backchannel, tokens, payload.RootElement);
46+
context.RunClaimActions();
47+
48+
await Events.CreatingTicket(context);
49+
return new AuthenticationTicket(context.Principal!, context.Properties, Scheme.Name);
50+
}
51+
52+
private static partial class Log
53+
{
54+
internal static async Task UserProfileErrorAsync(ILogger logger, HttpResponseMessage response, CancellationToken cancellationToken)
55+
{
56+
UserProfileError(
57+
logger,
58+
response.StatusCode,
59+
response.Headers.ToString(),
60+
await response.Content.ReadAsStringAsync(cancellationToken));
61+
}
62+
63+
[LoggerMessage(1, LogLevel.Error, "An error occurred while retrieving the user profile: the remote server returned a {Status} response with the following payload: {Headers} {Body}.")]
64+
private static partial void UserProfileError(
65+
ILogger logger,
66+
System.Net.HttpStatusCode status,
67+
string headers,
68+
string body);
69+
}
70+
}
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
3+
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
4+
* for more information concerning the license and the contributors participating to this project.
5+
*/
6+
7+
using System.Linq;
8+
using System.Security.Claims;
9+
using static AspNet.Security.OAuth.Atlassian.AtlassianOAuthenticationConstants;
10+
11+
namespace AspNet.Security.OAuth.Atlassian;
12+
13+
public partial class AtlassianAuthenticationOptions : OAuthOptions
14+
{
15+
public AtlassianAuthenticationOptions()
16+
{
17+
ClaimsIssuer = AtlassianAuthenticationDefaults.Issuer;
18+
CallbackPath = AtlassianAuthenticationDefaults.CallbackPath;
19+
20+
AuthorizationEndpoint = AtlassianAuthenticationDefaults.AuthorizationEndpoint;
21+
TokenEndpoint = AtlassianAuthenticationDefaults.TokenEndpoint;
22+
UserInformationEndpoint = AtlassianAuthenticationDefaults.UserInformationEndpoint;
23+
24+
AdditionalAuthorizationParameters.Add("audience", "api.atlassian.com");
25+
AdditionalAuthorizationParameters.Add("prompt", "consent");
26+
27+
Scope.Add("read:me");
28+
29+
ClaimActions.MapJsonKey(ClaimTypes.NameIdentifier, "account_id");
30+
ClaimActions.MapJsonKey(ClaimTypes.Email, "email");
31+
ClaimActions.MapJsonKey(ClaimTypes.Name, "name");
32+
33+
ClaimActions.MapJsonKey(Claims.AccountType, "account_type");
34+
ClaimActions.MapJsonKey(Claims.Picture, "picture");
35+
ClaimActions.MapJsonKey(Claims.AccountStatus, "account_status");
36+
ClaimActions.MapJsonKey(Claims.Nickname, "nickname");
37+
ClaimActions.MapJsonKey(Claims.ZoneInfo, "zoneinfo");
38+
ClaimActions.MapJsonKey(Claims.Locale, "locale");
39+
40+
ClaimActions.MapJsonSubKey(Claims.JobTitle, "extended_profile", "job_title");
41+
ClaimActions.MapJsonSubKey(Claims.Organization, "extended_profile", "organization");
42+
ClaimActions.MapJsonSubKey(Claims.Department, "extended_profile", "department");
43+
ClaimActions.MapJsonSubKey(Claims.Location, "extended_profile", "location");
44+
}
45+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
// Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
2+
// See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
3+
// for more information concerning the license and the contributors participating to this project.
4+
5+
namespace AspNet.Security.OAuth.Atlassian;
6+
7+
public static class AtlassianOAuthenticationConstants
8+
{
9+
public static class Claims
10+
{
11+
public static readonly string AccountType = "urn:atlassian:account_type";
12+
public static readonly string Picture = "urn:atlassian:picture";
13+
public static readonly string AccountStatus = "urn:atlassian:account_status";
14+
public static readonly string Nickname = "urn:atlassian:nickname";
15+
public static readonly string ZoneInfo = "urn:atlassian:zoneinfo";
16+
public static readonly string Locale = "urn:atlassian:locale";
17+
public static readonly string JobTitle = "urn:atlassian:job_title";
18+
public static readonly string Organization = "urn:atlassian:organization";
19+
public static readonly string Department = "urn:atlassian:department";
20+
public static readonly string Location = "urn:atlassian:location";
21+
}
22+
}

0 commit comments

Comments
 (0)