It works in VB.NET but not in C#

So I have two other topics open, one was a general question regarding getting documentation for a Windows program and the other was about registering a Windows program, and since posting those I have been researching what my problem might be - the API simply wasn’t working for a variety of reasons.

Someone on stackoverflow posted a question titled “Connecting to asana using webrequests” and since it was a comprehensive example in code that I understand (VB.NET, though my application is C#) I copied it into a new project, ran it, and it worked. Then I created a new C# project, converted the code, ran it and it did not work. The error is 401 unauthorized.

Why would I be authorized in the VB.NET version of the code but not in C#?

That does sound odd. Are you able to post the code for both projects?

VB.NET:
Imports System.Net
Imports System.IO
Imports System.Text

’ Use this to verify your JSON https://jsonlint.com/

’ Connecting to asana using webrequests

Public Class frmAsanaAPI
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim response As String ’ = GetResponse(“Log in - Asana”)
response = GetResponse(“https://app.asana.com/api/1.0/users/me”)
MsgBox(response)
'response = GetResponse(“https://app.asana.com/api/1.0/workspaces”)

    response = GetResponse("https://app.asana.com/api/1.0/tasks/x/stories?opt_pretty")
End Sub

Public Function GetResponse(uri As String, Optional data As String = "", Optional method As String = "GET") As String
    System.Diagnostics.Trace.WriteLine(uri)

    ' Thanks to this person, https://stackoverflow.com/questions/39906750/cant-deserialize-asana-user-data-into-c-sharp-class, I know I have to do this (even though it's no C# anymore...)
    ' ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;
    ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12

    ' create request
    Dim request As HttpWebRequest = DirectCast(WebRequest.Create(uri), HttpWebRequest)
    request.PreAuthenticate = True
    request.Method = method
    request.ContentType = "application/x-www-form-urlencoded"

    ' log in
    Dim authInfo As String = "x/xxxxxxxxxxxxx" & ":" & ""
    ' blank password
    authInfo = Convert.ToBase64String(Encoding.[Default].GetBytes(authInfo))
    request.Headers("Authorization") = "Basic " & authInfo

    ' send data
    If data <> "" Then
        Dim paramBytes As Byte() = Encoding.ASCII.GetBytes(data)
        request.ContentLength = paramBytes.Length
        Dim reqStream As Stream = request.GetRequestStream()
        reqStream.Write(paramBytes, 0, paramBytes.Length)
        reqStream.Close()
    End If

    ' get response
    Try
        Dim response As HttpWebResponse = DirectCast(request.GetResponse(), HttpWebResponse)
        Return New StreamReader(response.GetResponseStream()).ReadToEnd()
    Catch ex As WebException
        Dim response As HttpWebResponse = DirectCast(ex.Response, HttpWebResponse)
        Throw New Exception((uri & " caused a " & CInt(response.StatusCode) & " error." & vbLf) + response.StatusDescription)
    End Try

End Function

End Class

C#
using System;
using System.Text;
using System.Net;
using System.Windows.Forms;
using System.IO;

namespace WindowsApplication2
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
string response;
response = GetResponse(“https://app.asana.com/api/1.0/users/me”);
}

    public string GetResponse(string uri, string data = "", string method = "GET")
    {
        ServicePointManager.SecurityProtocol = SecurityProtocolType.Tls12;

        // Create Request
        HttpWebRequest request = (HttpWebRequest)WebRequest.Create(uri);
        request.PreAuthenticate = true;
        request.Method = method;
        request.ContentType = "application/x-www-form-urlencoded";

        // Log In
        string authInfo = "x/xxxxxxxxxxxxxx" + ":" + "";
        authInfo = Convert.ToBase64String(Encoding.Default.GetBytes(authInfo));
        request.Headers["Authorization"] = "Basic" + authInfo;

        // send data if supplied
        if (data != "")
        {

        }

        // receive response
        HttpWebResponse response = (HttpWebResponse)request.GetResponse();
        return new StreamReader(response.GetResponseStream()).ReadToEnd();

    }
}

}

One thing I see off the bat is there’s a space missing following “Basic” in the C# implementation of

request.Headers["Authorization"] = "Basic" + authInfo;

which is present in the VB.NET implementation.

I am idiot! And/or you have younger eyes than I!
Yes, that is it. They are both working now.
Can I just ask, because it was a long road to this perhaps simple, obvious code - why was it so hard to find this? I was trying code with client id’s and secrets and none of it was needed and didn’t work. This person’s VB.NET code was the only comprehensive example that I could run successfully.

These are different methods of authentication. In your code use are using what is called Basic Auth. The downside of this method is that you need to hard code (or provide) login + password inside your code. It means, that if your code is going to be stolen by someone, they will have a full access to your account.

Another method is oAuth : you don’t need login and password (and you don’t send them via open protocols as in Basic Auth and e.g. main-in-the-middle attack cannot be done to reveal your credentials). You just have a temporary token (together with secret to be able to verify that this is the correct client) which allows you to access only to resources that this token is authorized for. It means, that this method is much more secure and now widely used.

In addition, using oAuth you can authenticate your user with popular services (as Google, Facebook, Twitter, …) without the need of creating your own authentication mechanism.

Excuse me for being naive, where in my code is there a login and password? Is it because I am running the application that the external app I am running can access asana as I’ve supplied id/pw separately? Or did you mean the PAT isn’t safe to hardcode and send along?

Of course I want my application to be as safe as possible. What else would the posted code need to use oAuth?

Hi @MMOCK,
According to your code, auth data is stored in authInfo variable.

OAuth-based authentication is a more complex process than what you’re using now which presumably is using a Personal Access Token - but OAuth is the more desirable approach if you’re building a solution that you plan to have other people use (as opposed to something just for you, in which case the PAT approach is fine).

You can read all about Asana OAuth authentication here.

OAuth is most typically done via a web server but can be done in a Windows desktop application. I’d recommend googling “OAuth Windows desktop application” and you’ll find lots of resources. For example, here are some code samples you might find helpful.

I am going to open a new thread to continue this. My plan was that I was always going to get back to this, I just wanted to have some fun using the API first since I was so happy to be connected! So I am displaying asana stories in my application. I have a command button to add a note. When a user tested this, her note was attributed to me! I guess now is the time I have to work more on authentication, so I don’t have the entire Customer Service department adding new notes authored by me. LOL!

On the upside, it would make you look like a very busy and productive person. :wink:

1 Like