JIRA OAuth Issues - c#

there
I want to use JIRA API to access data from my company's JIRA instance. The steps of login process on internet browser are:
Type https://mycompanyname.atlassian.net
Direct to login page.
Type my company email address
It re-directly to Microsoft authentication page, click "Continue".
Type password of my Azure AD domain account.
Select a method to verify my identify (I select way of verifying code sent to my bind mobile)
Bingo!
My questions are:
1.How can I finish above steps by C# code?
2.Does my application need the mobile text code verification for every instance?
FYI:
I want to put my application on Azure function as a timer trigger to run some data from JIRA. However, I get stuck at first step-authentication.

I do not know if you have already solved your issue. But from an azure function there is a few steps you will need to take.
In Azure use Managed Identity, you will need this for function to get some details you will save into Keyvault
Have a Keyvault ready cause you gonna need it to get some secrets out of it
Create your Function app and install Atlassian.SDK - can see documentation here
In the dependency injection of your function call
var config = new ConfigurationBuilder()
.AddAzureKeyVault(secretClient, new KeyVaultSecretManager())
.AddEnvironmentVariables()
.Build();
container.Register(p => Options.Create(config.Get<ConfigDetailsModel>()))
.SingleInstance();
In your Jira service use the following to signin
public JiraService(IOptions<ConfigDetailsModel> config)
{
_config = config.Value;
}
var jira = Jira.CreateRestClient(_config.JiraUri, _config.JiraUserName, _config.JiraApiKey);
var result = await jira.Projects.GetProjectsAsync();
Go to https://id.atlassian.com/manage-profile/security/api-tokens and create an API key for your password as plaintext passwords are not supported anymore by Jira.
Save your username, password and url into keyvault, make password expire at some point.
This should work. Unfortunately the OAUTH way, more secure than the basic way is not really geared towards system-to-system way, well not that I could find, as it requires a user input to allow verification code. If you do not mind moving the flow out of function and into a user flow with OAuth then best article I found is here, but you need to copy additional things from his github to get it to work. I got this flow working in Blazor, and tried to get it to work in my Servicebus triggered function.
The only way I could get the Jira stuff to work in function for service-to-service was to use basic Auth either via the Atlassian.SDK or Httpclient
Note SDK makes life somewhat easier than writing own http client stuff
var client = new HttpClient
{
BaseAddress = new Uri(_config.JiraUri)
};
client.DefaultRequestHeaders.Accept.Clear();
var authenticationString = $"{_config.JiraUSerName}:{_config.JiraApiKey}";
var base64EncodedAuthenticationString = Convert.ToBase64String(System.Text.ASCIIEncoding.ASCII.GetBytes(authenticationString));
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", base64EncodedAuthenticationString);
var project = await client.GetAsync($"rest/agile/1.0/epic/{projectId}");
var content = await project .Content.ReadAsStringAsync();
UPDATE
When you create an API key in Jira for use in code, know that the API key in JIRA is tied to the person who created it.
It should be an Admin account
Create a global break glass account to manage this, you do not want an apikey tied to a user that might leave in a few months
This break glass account should belong to your DevOps team and no one else who can exploit this api key

Related

How can I authorize a .NET 5.0 / C# application to interact with Microsoft 365 SharePoint Online without user interaction?

I need to create a webservice which will accept a file from a user, parse the file for data, assemble a collection of files stored on Sharepoint, and the return a zip file containing those files to the user.
The user may not have direct access to Sharepoint and in the future there might not even be a human user involved as the file may be pulled automatically from another system on a timer.
I am creating the solution in C# and am struggling to create a client which can successfully authenticate against Sharepoint.
Currently, I am trying to use the CSOM Authentication Manager in the Microsoft documentation, but using the Visual Studio debugger, I observe a failure in this method:
private async Task<string> AcquireTokenAsync(Uri resourceUri, string username, string password)
{
string resource = $"{resourceUri.Scheme}://{resourceUri.DnsSafeHost}";
var clientId = defaultAADAppId;
var body = $"resource={resource}&client_id={clientId}&grant_type=password&username={HttpUtility.UrlEncode(username)}&password={HttpUtility.UrlEncode(password)}";
using (var stringContent = new StringContent(body, Encoding.UTF8, "application/x-www-form-urlencoded"))
{
var result = await httpClient.PostAsync(tokenEndpoint, stringContent).ContinueWith((response) =>
{
return response.Result.Content.ReadAsStringAsync().Result;
}).ConfigureAwait(false);
var tokenResult = JsonSerializer.Deserialize<JsonElement>(result);
var token = tokenResult.GetProperty("access_token").GetString();
return token;
}
}
The value of result is:
"{"error":"unauthorized_client","error_description":"AADSTS700016: Application with identifier '986002f6-c3f6-43ab-913e-78cca185c392' was not found in the directory 'enpal.de'. This can happen if the application has not been installed by the administrator of the tenant or consented to by any user in the tenant. You may have sent your authentication request to the wrong tenant.\r\nTrace ID: d3b47a0b-54bc-40fd-a731-b99732326200\r\nCorrelation ID: 158621cc-3052-4c7b-9532-e2bab5c3cc09\r\nTimestamp: 2020-11-18 13:46:39Z","error_codes":[700016],"timestamp":"2020-11-18 13:46:39Z","trace_id":"d3b47a0b-54bc-40fd-a731-b99732326200","correlation_id":"158621cc-3052-4c7b-9532-e2bab5c3cc09","error_uri":"https://login.microsoftonline.com/error?code=700016"}"
The resourceUri, username, and password values correctly correspond to the values I would directly use to log into the website. My administrator has assured me the user is an admin of the site and therefore should have all the possible permissions it should need to do anything, including connect.
Does anyone know how to make this work?
Or know a different solution which is more likely to work?
Or know of anything which must be configured on the SharePoint side to allow this solution to work?
It looks like you took the code sample from the Microsoft website and skipped on reading an important section: Configuring an application in Azure AD ;)
At the moment, you are trying to connect to your tenant as an app with an id of '986002f6-c3f6-43ab-913e-78cca185c392', which is a placeholder from the code sample. Register your app in your tenant, and insert the proper app id in your defaultAADAppId constant.

Azure Function with AD auth results in 401 Unauthorized when using Bearer tokens

I have a very simple Azure function in C# for which I've setup Azure AD Auth. I've just used the Express settings to create an App registration in the Function configuration.
public static class IsAuthenticated
{
[FunctionName("IsAuthenticated")]
public static async Task<IActionResult> Run(
[HttpTrigger(AuthorizationLevel.Anonymous, "get", "options", Route = null)]
HttpRequest req,
ILogger log)
{
return new OkObjectResult("You are " + req.HttpContext.User.Identity.Name);
}
}
When I access the function in my browser everything works as expected (if not logged in I have to login and get redirected to my API). But if I try to access my function anywhere a Bearer token is needed I get an 401 Unauthorized error. Even weirder I also can't execute the function in the Azure Portal.
But the token was aquired without a problem and added to the request:
I've tried a few different things to solve this problem. First I thought maybe it's a CORS problem (since I've also had a few of those) and just set CORS to accept *, but nothing changed.
Then I've added my API login endpoints to the redirect and tried setting the implicit grant to also accept Access tokens, it's still not working.
Is there anything I've overlooked? Shouldn't the App registration express config just work with azure functions?
EDIT:
Putting the URL to my function app in the redirects as suggested by #thomas-schreiter didn't change anything (I've tried the config in the screenshot and also just putting each of those values on it's own).
EDIT 2:
I've now also tried to aquire an Bearer token the manual way with Postman, but I still run into a 401 when calling my API.
UPDATE 2020-05-12: According to ambrose-leung's answer further below you can now add a custom issuer URL which should potentially enable you to use v2 tokens. I haven't tried this myself, but maybe this will provide useful for someone in the future. (If his answer helped you please give him an upvote and maybe leave a comment 😉)
This took forever to figure out, and there is very little information about this in the offical documentations.
But it turns out the problem was/is that Azure Functions don't support Bearer tokens generated by the oauth2/v2.0/ Azure API. Since the portal uses those (if your AD supports them) you are out of luck to be able to run the function in there.
This also explains why my postman requests didn't work, because I was also using the v2 api. After switching to v1 I could access my API (Postman doesn't allow you to add a resource_id when you use the integrated auth feature, so I had to switch to handling everything manually).
After that came the realisation that you can't use MSAL either if you are writing a JS client (Angular in my case). So one alternative is ADAL, where the Angular implementation looks kind of awkward. So I decided to use angular-oauth2-oidc which took another hour of tinkering to get it to play nicely with Azure AD.
But after all that I can finally access my API.
I really don't understand why you wouldn't allow users to access Azure Function Apps with Azure AD v2 tokens, but at least this should be so much better documented. But whatever, I can finally go to sleep.
EDIT: After I opend an issue for this, they added a note that v2 isn't supported by Azure Functions, hopefully making life easier for other people.
https://learn.microsoft.com/en-us/azure/app-service/configure-authentication-provider-aad
I managed to get it working through postman using following configuration.
Important lesson was setting in "Allowed token audiences" and "resource" name used in postman to acquire token should be same in this case. I used the same code provided here in question. in this case app registered in Azure AD is a client and resource as well. configuration and testing through postman as follows
Acquire token in postman
Calling azure function using Postman .. Authorization header with bearer token
You can now use v2.0 tokens!
Instead of choosing 'Express' when you configure AAD, you have to choose 'Advance' and add the /v2.0 part at the end of the URL.
This is the code that I use in my console app to present the user with a login prompt, then take the bearer token for use with the Azure Function.
string[] scopes = new string[] { "profile", "email", "openid" };
string ClientId = [clientId of Azure Function];
string Tenant = [tenantId];
string Instance = "https://login.microsoftonline.com/";
var _clientApp = PublicClientApplicationBuilder.Create(ClientId)
.WithAuthority($"{Instance}{Tenant}")
.WithDefaultRedirectUri()
.Build();
var accounts = _clientApp.GetAccountsAsync().Result;
var authResult = _clientApp.AcquireTokenInteractive(scopes)
.WithAccount(accounts.FirstOrDefault())
.WithPrompt(Prompt.SelectAccount)
.ExecuteAsync().Result;
var bearerTokenForAzureFunction = authResult.IdToken;
When setting up your Active Directory authentication on your Function App, set management mode to advanced and fill in the Client ID and Issuer URL as required (and the client secret if necessary).
Importantly, under the Allowed Token Audiences, enter the Application ID URI. This can be found in your registered App Registration (in your AD) under the Expose an API option.
This is what I was missing to get authentication working on my Function App. Before I added that token audience, I would always get a 401 with a valid access token.
This Azure active directory - Allow token audiences helped me get my answer but it took me a while to realise what it was referring to. Remember, it's the Application ID URI that can be found within your App Registration.
I hope it helps!
If you are banging your head against the wall like myself and the original poster, it may be that you are allowing users to sign in from "Accounts in any organizational directory (Any Azure AD directory - Multitenant) and personal Microsoft accounts (e.g. Skype, Xbox)."
Note that as of May 2021, v2.0 works perfectly. If you use https://login.microsoftonline.com/TENANT_ID/oauth2/v2.0/token to get a token with Postman (as described above), you will get a valid token that you can use to auth your AZ Function with.
With that said, IF a user is signed in via a personal account or an account not within your AAD, the token call made by MSAL is requested with the default Microsoft tenant id, NOT your tenant id.
THIS is why I was unable to auth my function. If you are logged in with a user in your tenant's AAD, MSAL is amazing and easy to use and everything will work as described in the documentation.
In the AAD app itself, go to Settings -> Reply URLs and verify that the url of the Function App is in the list, which has the following format: https://mycoolapp.azurewebsites.net. If it isn't, then add it.
If you use slots, you have to add it for both slots.
The only thing i can think of right now is Allowed Audience.
Go to Your Active directory settings and click Advance. Under Allowed Token Audience
Add your exact function url. It might already be there with call back url but Simply replace it with only function base url without any call back as mentioned in the picture.
Make sure when you press ok , you also save your Authentication / Authorization setting to take effect and try again after 1min or so. I tested using PostMan and passing bearer token and it works !
I'm facing the exact same issue today. The issue turned out to be the resource id that I was passing when requesting the access token.
For example, initially I was requesting a token like this, using the function URL as the resource id:
AuthenticationResult authenticationResult = authenticationContext.AcquireTokenAsync("https://myfunction.azurewebsites.net", "myClientAppIdGUID", new Uri("https://login.live.com/oauth20_desktop.srf"), new PlatformParameters(PromptBehavior.SelectAccount)).Result;
While this returned an access token, I was receiving a 401 unauthorized when using the access token to call my function api.
I changed my code to pass my function apps App Id as the resource:
AuthenticationResult authenticationResult = authenticationContext.AcquireTokenAsync("myFunctionAppIdGUID", "myClientAppIdGUID", new Uri("https://login.live.com/oauth20_desktop.srf"), new PlatformParameters(PromptBehavior.SelectAccount)).Result;
Everything works fine now.
For me this was solved when I added the scope: [clientId]/.default
per this article

AuthenticationToken is null

I'm currently writing a C# metro app for the Windows 8 consumer preview which fetches some data from my REST-based web services. I want the app to authenticate against the services using the Windows Live account of the current user. Therefore, I added the Windows Live SDK to my solution and pasted the following snippet from the documentation into my login view:
LiveAuthClient liveClient = new LiveAuthClient();
LiveLoginResult loginResult = await liveClient.Login(new string[] { "wl.signin" });
After the login call has succeeded, I want to pass the encrypted AuthenticationToken of the LiveConnectSession via SSL to my webservice which should decrypt the token and read the information it is interested in (that's what the documentation suggests for such a SSO scenario). But sadly, the AuthenticationToken property of the session is always null. Am I missing something here?
I ran into the same problem and realised I had two issues with my configuration:
I didn't have a "Redirect domain" defined in the API settings of https://manage.dev.live.com
I wasn't using the overloaded LiveAuthClient constructor
For example in the API settings you specify:
Redirect domain: http://localhost/myapp
You then use the constructor overload of the LiveAuthClient:
var authClient = new LiveAuthClient("http://localhost/myapp");
var loginResult = await authClient.LoginAsync("wl-signin");
//this should no longer be null
var authToken = loginResult.Session.AuthenticationToken;
The redirect URI doesn't need to point to a working endpoint from what I can tell, as long as the two values match you should be in business.
Have you registered your app on the Live Connect app management site for Metro style apps? You need to register it here for it to work with Live Services. It will give you following instructions after you have given the app package a name and publisher.

Trouble getting test user information

I'm trying to use the Facebook SDK 5.2.1 to ultimately create a test user, however even what I believe is the simple example of getting the list of test accounts isn't working for me. I get the OAuthException "An access token is required to request this resource."
Here's my code (replace APP ID and APP SECRET with my own):
FacebookOAuthClient oauth = new FacebookOAuthClient { AppId = "APP ID", AppSecret = "APP SECRET" };
dynamic tokenRes = oauth.GetApplicationAccessToken();
fbClient = new FacebookClient(tokenRes.access_token);
dynamic response = fbClient.Get("APPID/accounts/test-users");
However, I get the exception on the fbClient.Get line.
Any idea as to what's wrong?
Thanks,
Chad
After hours of trying various things and reading various web pages/blogs, I found the reason it wasn't working. In my app settings, I had my app type set to a Native/Desktop App. Changing this to Web, allows the above scenario to work. I'm not yet quite sure of what other differences exist between web vs native facebook apps. My app is certainly only being used via a desktop application and I can't understand why I need to set this to Web just to allow me to create test users.
This code works in my app:
var app = new FacebookClient(FacebookApplication.Current.AppId,
FacebookApplication.Current.AppSecret);
dynamic result = app.Post(string.Format("{0}/accounts/test-users",
FacebookApplication.Current.AppId),
new { installed = true, permissions = "user_about_me" });
The reason why you are receiving the exception OAuthException is because you have not yet got the permission of the user.
To do a Graph API call on the current user, you need to get the user to accept the permissions that you require FIRST and then do the Graph API call.
You need to get the user to a browser some how in your application, as there is not an authentication flow which doesn't require a browser window.
Check out this URL to view the authentication flows:
http://developers.facebook.com/docs/authentication/

How to get Google Analytics data using OAuth?

Hy guys, we are developing a system which will provide users with access to Google Analytics. I'm trying to implement it in the way so user don't need to enter their Google login credentials on our site, so trying to get it work using their login.
I have a solution which gets analytics using user's email and password. I'm looking for a solution which will not require user's email and password but can not find anything.
How can it be done? any advices or links will be appreciated.
thanks
Ok, guys, after a few days of struggle I finally figured this out. There is no documentation on the Internet and people who had done it before did not want to share their success by some reason. I found this discussion which helped me.
To make it work you will need DotNetOpenAuth from http://www.dotnetopenauth.net/ and gdata from http://code.google.com/p/google-gdata/
so
using DotNetOpenAuth.ApplicationBlock;
using DotNetOpenAuth.OAuth;
using Google.GData.Client;
using Google.GData.Analytics;
In DotNetOpenAuth there is sample project named OAuthConsumer which you need.
Change it to requiest authorization for Analytics:
GoogleConsumer.RequestAuthorization(google, GoogleConsumer.Applications.Analytics);
This will get you Token and Token secret.
You can use them like this:
GOAuthRequestFactory requestFactory = new GOAuthRequestFactory("cp", TokenManager.ConsumerKey); //ConsumerKey actually is the name of web application
requestFactory.ConsumerKey = TokenManager.ConsumerKey;
requestFactory.ConsumerSecret = TokenManager.ConsumerSecret;
requestFactory.Token = AccessToken;
requestFactory.TokenSecret = TokenManager.GetTokenSecret(AccessToken);
requestFactory.UseSSL = true;
AnalyticsService service = new AnalyticsService(requestFactory.ApplicationName); // acually the same as ConsumerKey
service.RequestFactory = requestFactory;
const string dataFeedUrl = "https://www.google.com/analytics/feeds/data";
DataQuery query1 = new DataQuery(dataFeedUrl);
This service you can use like here or here
And the last thing, you WILL NOT be available to try and test it on localhost so you will need a domain which MUST be registered with Google here in order to get consumer key and secret
There's a .NET/C# class for Google Data authentication that can be used to access the Google Analytics Data Export API (since the API is part of the Google Data standard, though you might need to make Google Analytics specific adjustments.)*
The authentication is best managed by creating a Google Registered Application, as this allows you to make the authentication without security warnings (and, for that matter, security lapses).
There are three forms of supported authentication; the 'secure'/passwordless ones are OAuth and AuthSub (which is the Google-proprietary version of OAuth); the hardcoded username and password version is referred to by Google as 'ClientLogin', and is not considered secure or ideal for multiple-user applications.
*(Since you tagged the question .netc#)
Edit: More details on using AuthSub or OAuth with the .NET library:
AuthSubSupport: http://code.google.com/p/google-gdata/wiki/AuthSubSupport
Code Samples on how to use the libraries for OAuth authentication: http://code.google.com/apis/gdata/docs/auth/oauth.html#2LeggedOAuth (Click the .NET tab).
Basics of working with OAuth are here: http://code.google.com/apis/accounts/docs/OpenID.html#working
Authenticating with OAuth: http://code.google.com/apis/accounts/docs/OAuth.html
After you authenticate a user with OAuth, you will have the request token that works like the one you get back from Google's login API. From there, it should be the same as username/password.
I don't think you need to mess with OAuth.
The google analytics api lets you pass credentials. Just start from this data feed example.
http://code.google.com/p/google-gdata/source/browse/trunk/clients/cs/samples/Analytics_DataFeed_Sample/dataFeed.cs
// Configure GA API and do client login Authorization.
AnalyticsService asv = new AnalyticsService("gaExportAPI_acctSample_v2.0");
asv.setUserCredentials(clientUser, clientPass);
Download the client library here
http://code.google.com/apis/analytics/docs/gdata/gdataLibraries.html
To get a feel for data queries, play with this and then copy the values into the example above
http://code.google.com/apis/analytics/docs/gdata/gdataExplorer.html

Categories

Resources