Android Question Trying to login using MS Graph

stu14t

Active Member
Licensed User
Longtime User
I've created the following resumable sub and made a bit of a mess of it and can't see what's wrong

The page calls the login page at Microsoft via the Graph Rest API but then I get the following error:

java.lang.ClassCastException: anywheresoftware.b4a.keywords.Common$ResumableSubWrapper cannot be cast to java.lang.String
:
B4X:
Sub Login As ResumableSub
    Dim WebView1 As WebView
    WebView1.Initialize("WebView1")
    Activity.AddView(WebView1, 0, 0, 100%x, 100%y)
    WebView1.LoadUrl("https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=XXXXXXXX-XXXX-XXXX-bde1-234b27c47ec8&response_type=code&redirect_uri=https://login.microsoftonline.com/common/oauth2/nativeclient&scope=user.read")

wait for WebView1_PageFinished (Url As String)
    If Url.Contains("https://login.microsoftonline.com/common/oauth2/nativeclient") Then
        Dim authCode As String
        Dim urlParts() As String
        urlParts = Regex.Split("https://login.microsoftonline.com/common/oauth2/nativeclient&code=&code=", Url)
        If urlParts.Length > 1 Then
            authCode = urlParts(1)
            Log(authCode)
            GetToken(authCode)
        Else
            Log("No Token Passed")
        End If
    End If
End Sub
 

stu14t

Active Member
Licensed User
Longtime User
Erel,

It's only a test app at the moment to try and get it working. Full code below:

B4X:
Sub Process_Globals
    'These global variables will be declared once when the application starts.
    'These variables can be accessed from all modules.
    Private xui As XUI
End Sub

Sub Globals
    'These global variables will be redeclared each time the activity is created.
End Sub

Sub Activity_Create(FirstTime As Boolean)
    Activity.LoadLayout("Layout")
End Sub

Sub Activity_Resume

End Sub

Sub Activity_Pause (UserClosed As Boolean)

End Sub

Sub Button1_Click
    Login
End Sub
Sub Login As ResumableSub
    Dim WebView1 As WebView
    WebView1.Initialize("WebView1")
    Activity.AddView(WebView1, 0, 0, 100%x, 100%y)
    WebView1.LoadUrl("https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=XXXXXXXX-XXXX-XXXX-XXXX-234b27c47ec8&response_type=code&redirect_uri=https://login.microsoftonline.com/common/oauth2/nativeclient&scope=user.read")

wait for WebView1_PageFinished (Url As String)
    If Url.Contains("https://login.microsoftonline.com/common/oauth2/nativeclient") Then
        Dim authCode As String
        Dim urlParts() As String
        urlParts = Regex.Split("https://login.microsoftonline.com/common/oauth2/nativeclient&code=&code=", Url)
        If urlParts.Length > 1 Then
            authCode = urlParts(1)
            Log(authCode)
            GetToken(authCode)
        Else
            Log("No Token Passed")
        End If
    End If
End Sub
Sub GetToken(authCode As String)
    Dim Http As HttpJob
    Http.Initialize("GetToken", Me)
    Http.Download2("https://login.microsoftonline.com/common/oauth2/v2.0/token", _
      Array As String("client_id", "XXXXXXXX-XXXX-XXXX-bde1-234b27c47ec8", "client_secret", "XXXXXXXX-XXXX-XXXX-825b-e356274f11df", "code", authCode, "redirect_uri", "https://login.microsoftonline.com/common/oauth2/nativeclient", "grant_type", "authorization_code"))
      
    If Http.Success Then
        Dim response As String
        response = Http.GetString
        Dim token As String
        token = response.SubString2(response.IndexOf("access_token") + "access_token".Length + 3, response.IndexOf(",""token_type""") - 2)
        Log(token)
        SaveToken(token)
    End If
    Http.Release
End Sub

Sub SaveToken(token As String)
    Dim prefs As SharedPreferences
    prefs.Initialize("MyApp")
    prefs.SaveString("Token",token)
End Sub
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
try like this:
B4X:
Sub button1_Click
    wait for (Login) complete(status As String)
    Log( "login result: " & status)
End Sub

Sub Login As ResumableSub
    Dim WebView1 As WebView
    WebView1.Initialize("WebView1")
    Activity.AddView(WebView1, 0, 0, 100%x, 100%y)
    WebView1.LoadUrl("https://login.microsoftonline.com/common/oauth2/v2.0/authorize?client_id=XXXXXXXX-XXXX-XXXX-XXXX-234b27c47ec8&response_type=code&redirect_uri=https://login.microsoftonline.com/common/oauth2/nativeclient&scope=user.read")

    wait for WebView1_PageFinished (Url As String)
    If Url.Contains("https://login.microsoftonline.com/common/oauth2/nativeclient") Then
        Dim authCode As String
        Dim urlParts() As String
        urlParts = Regex.Split("https://login.microsoftonline.com/common/oauth2/nativeclient&code=&code=", Url)
        If urlParts.Length > 1 Then
            authCode = urlParts(1)
            Log(authCode)
            wait for (GetToken(authCode)) complete(token As String)
            Return(token)
        Else
            Log("No Token Passed")
            Return("No token passed")
        End If
    End If
End Sub

Sub GetToken(authCode As String) As ResumableSub
    Dim Http As HttpJob
    Http.Initialize("GetToken", Me)
    Http.Download2("https://login.microsoftonline.com/common/oauth2/v2.0/token", _
      Array As String("client_id", "XXXXXXXX-XXXX-XXXX-bde1-234b27c47ec8", "client_secret", "XXXXXXXX-XXXX-XXXX-825b-e356274f11df", "code", authCode, "redirect_uri", "https://login.microsoftonline.com/common/oauth2/nativeclient", "grant_type", "authorization_code"))
    
    wait for (Http) JobDone( Http As HttpJob)
    
    If Http.Success Then
        Dim response As String
        response = Http.GetString
        Http.Release
        Dim token As String
        token = response.SubString2(response.IndexOf("access_token") + "access_token".Length + 3, response.IndexOf(",""token_type""") - 2)
        Log(token)
        SaveToken(token)
        Return(token)
    Else
        Return("gettoken failed")
    End If
 End Sub

Sub SaveToken(token As String)
    Dim prefs As SharedPreferences
    prefs.Initialize("MyApp")
    prefs.SaveString("Token",token)
End Sub

result (without proper key) attached. you've got a couple nested layers of resumable subs. i would have preferred testing with actual credentials, but i did receive the expected answer. builds without error and runs. see what happens when you stuff your credentials.
 

Attachments

  • 1.png
    1.png
    67.4 KB · Views: 74
Upvote 0

stu14t

Active Member
Licensed User
Longtime User
Thanks for the response.

The code builds fine but it doesn't wait for the WebView1_PageFinished and just executes the code. I don't know if the token is passed back or not when I'm done logging in as the sub is finished and returns No Token Passed.
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
i took your code (which didn't work, according to your post) and made it work by addressing the resumable subs parts. i didn't change the code flow (i don't think i did, anyway).
as to whether or not your code is correct or actually does what you think it does, i can't say. as a guess, i would suggest waiting for _pagefinished may not be the correct place to wait for something (in this case). because i do not have proper credentials, i can only guess what is you might be seeing. i would add some more logging. i would also remove the nested resumable subs until i knew for sure what was coming back (you may have already done this, i don't know)

update: something else occurred to me: loading the initial webview already having credentials struck me as an unusual approach. but, again, without credentials, i have no way of knowing exactly what you're expecting back. and - no offense - don't tell me what you're expecting. if i can't see for myself it's immaterial. if you know for sure that loading the webview is the way to go, fine. i can't help but thinking that okhttputils2 even for the initial contact is something you might want to consider.
 
Last edited:
Upvote 0

stu14t

Active Member
Licensed User
Longtime User
Many thanks for the guidance. I'll keep looking at it and I think you are correct about the nested resumable subs. There is a whole lot of password entry 2FA that is getting ignored.

Thanks for taking the time @drgottjr
 
Upvote 0

drgottjr

Expert
Licensed User
Longtime User
just a reminder: webviewclient's onPageFinished() method
"is called only for main frame" (android documentation).
you've got something going on with that redirect of yours.
my guess is that's the page you're waiting for to trigger your
auth key extraction. it's possible onPageFinished() is never
called for the page that is loaded as a result of a redirection.

if you've tested this and know for a fact that it works, i stand
corrected. otherwise, you need to isolate that very first
loadurl() and wait for(), and see if the redirection fires an
onPageFinshed() (or _pagefinished in b4a). you might only
get the initial page (which means your app will never succeed).
you might - just might - get 2 _pagefinished events! that means
you have to ignore the first one and wait for the second.

if you notice in your login sub, you don't do 2 important things:
1) you never actually log the url that triggers the _pagefinished event. so you don't know what you got.
2) you do an "If Url.Contains("https://login.microsoftonline.com/common/oauth2/nativeclient") Then...", but you don't do an "else". here again, you leave yourself blind. if the url doesn't contain something you're looking for, what does it contain? you don't bother toask...

i think you need to see how many _pagefinished events you get and what the url is that is passsed by the event(s).
 
Last edited:
Upvote 0
Top