Android Question REST Client Authorization Denied

coldtech

Member
Licensed User
Longtime User
I have written a simple WebAPI server and a client in B4A. Using Postman, the client on a simulator and a quick and dirty C# client all work fine. But when I try it from an actual device it fails with an Authorization has been denied error. I have tried a Samsung S7 and a Google Pixel both internal and externally. If I remove the [Authorize] attributes from the server it returns the desired results also.

I am totally perplexed by this behavior, could anyone possibly provide me some insight?
 

DonManfred

Expert
Licensed User
Longtime User
You are missing an very important thing:
- Posting all relevant infos! B4A Code which fails, for reference the working c# code.

How do you expect to get help with the informations you provide?
 
Upvote 0

coldtech

Member
Licensed User
Longtime User
Sorry it's so trivial I didn't think to post it.

Sub BuildList(URL As String, OClass As String)
'ToastMessageShow(URL & " " & ObjectClass, True)

Dim Job As HttpJob

LV.Clear
ObjectClass = OClass

Job.Initialize("ListJob", Me)

Job.Username = Main.manager.GetString("Login")
Job.Password = Main.manager.GetString("Password")
Job.Download(URL)

Wait For (Job) JobDone(Job As HttpJob)

If Job.Success Then
FillListView(Job.GetString)
Else
Log("Job Failed: " & Job.ErrorMessage)
ToastMessageShow("Job Failed: " & Job.ErrorMessage, True)
End If
Job.Release

End Sub
 
Upvote 0

coldtech

Member
Licensed User
Longtime User
You would think that the authentication is wrong but it runs via the emulator just fine. Here is a simple C# client that also works.

B4X:
using System;
using System.Net;
using System.Net.Http;
using System.Text;
using System.Threading.Tasks;
namespace resttest
{
    class Program
    {
        static void Main(string[] args)
        {
            Task t = new Task(HTTP_GET);
            t.Start();
            Console.ReadLine();
        }
        static async void HTTP_GET()
        {
            var TARGETURL = "http://www.XXXX.com:95/api/stores";
            HttpClientHandler handler = new HttpClientHandler();
           
            Console.WriteLine("GET: + " + TARGETURL);
            // ... Use HttpClient.           
            HttpClient client = new HttpClient(handler);
            var byteArray = Encoding.ASCII.GetBytes("XXX:XXX");
            client.DefaultRequestHeaders.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Basic", Convert.ToBase64String(byteArray));
            HttpResponseMessage response = await client.GetAsync(TARGETURL);
            HttpContent content = response.Content;
            // ... Check Status Code                               
            Console.WriteLine("Response StatusCode: " + (int)response.StatusCode);
            // ... Read the string.
            string result = await content.ReadAsStringAsync();
            // ... Display the result.
            if (result != null &&
            result.Length >= 50)
            {
                Console.WriteLine(result.Substring(0, 50) + "...");
            }
        }
    }
}
 
Upvote 0

coldtech

Member
Licensed User
Longtime User
Here is the entire B4A module:

B4X:
#Region  Activity Attributes
    #FullScreen: False
    #IncludeTitle: False
#End Region

Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.

End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
    'These variables can only be accessed from this module.
    Dim ObjectClass As String
    Dim LV As ListView
    
    Private Toolbar As Panel
    Private Caption As Label
    
End Sub

Sub Activity_Create(FirstTime As Boolean)
    'Do not forget to load the layout file created with the visual designer. For example:
    Activity.LoadLayout("ListHandlerLayout")

End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub BuildList(URL As String, OClass As String)
    'ToastMessageShow(URL & "  " & ObjectClass, True)
    
    Dim Job As HttpJob
    
    LV.Clear
    ObjectClass = OClass
    
    Job.Initialize("ListJob", Me)
    
    Job.Username = Main.manager.GetString("Login")   
    Job.Password = Main.manager.GetString("Password")

    Job.Download(URL)
    
    Wait For (Job) JobDone(Job As HttpJob)
    
    If Job.Success Then
        FillListView(Job.GetString)
    Else
        Log("Job Failed: " & Job.ErrorMessage)
        ToastMessageShow("Job Failed: " & Job.ErrorMessage, True)
    End If   
    Job.Release
    
End Sub


Sub LV_ItemClick (Position As Int, Value As Object)
    ToastMessageShow(Value, True)
End Sub

Sub FillListView(Data As String)
    Dim JP As JSONParser
    JP.Initialize(Data)
    
    Caption.Text = ObjectClass
    Dim root As List = JP.NextArray
    For Each colroot As Map In root
        Dim Id As String = colroot.Get("Id")
        Dim Name As String = colroot.Get("Name")
        
        LV.SingleLineLayout.Label.TextColor = Colors.Black
        LV.AddSingleLine2(Name, Id)
    Next
    
End Sub
 
Upvote 0

DonManfred

Expert
Licensed User
Longtime User
As i wrote i do not know c#

BUT
client.DefaultRequestHeaders.Authorization
Looks like c# is generating an Request Header named Authorization

Using Username and password does not use any Header.
var byteArray = Encoding.ASCII.GetBytes("XXX:XXX");

Try it this way

B4X:
    Dim su As StringUtils
    Dim login As String = $"${Main.manager.GetString("Login")}:${Main.manager.GetString("Password")}"$
    Dim Job As HttpJob
    Job.Initialize("ListJob", Me)
  Job.Download("URL")
    Job.GetRequest.SetHeader("Authorization","Basic "&su.EncodeBase64(login.GetBytes("ASCII")))
    Wait For (Job) JobDone(Job As HttpJob)
    
    If Job.Success Then
    Else
        Log("Job Failed: " & Job.ErrorMessage)
        ToastMessageShow("Job Failed: " & Job.ErrorMessage, True)
    End If
    Job.Release
 
Upvote 0

coldtech

Member
Licensed User
Longtime User
Just wrote a simple Xamarin app to test it and it worked flawlessly on the device. I am at a total loss.

B4X:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Xamarin.Forms;
using Newtonsoft.Json;
using System.Net.Http;
using System.Collections.ObjectModel;
using System.Net.Http.Headers;

namespace XApp
{
    public class Stores
    {
        public string Id { get; set; }
        public string Name { get; set; }
        public string CustomerRSN { get; set; }
        public string RegionRSN { get; set; }
        public string ZoneRSN { get; set; }
    }

    public partial class MainPage : ContentPage
    {
        private const string url = "http://xxxxxx/api/stores";
        private HttpClient _Client = new HttpClient();       
        private ObservableCollection<Stores> _store;

        public MainPage()
        {
            InitializeComponent();
        }

        protected override async void OnAppearing()
        {
            var authData = string.Format("{0}:{1}", "xxxxx", "xxxxx");
            var authHeaderValue = Convert.ToBase64String(Encoding.UTF8.GetBytes(authData));
            _Client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", authHeaderValue);

            var content = await _Client.GetStringAsync(url);
            var store = JsonConvert.DeserializeObject<List<Stores>>(content);
            _store = new ObservableCollection<Stores>(store);
            The_List.ItemsSource = _store;
            base.OnAppearing();
        }
    }
}
 
Upvote 0

coldtech

Member
Licensed User
Longtime User
I just got issued a new S8 and tried it. It works on this device. So does anyone have any thoughts on why an S7 and a Google Pixel would not?

The S7 and S8 both have Android 8.0.0 but the kernel versions are different 4.4.78 vs 3.18.71. All 3 use Verizon as the carrier.
 
Upvote 0
Top