elisaado / somtoday-api-docs

Documentation for the SOMtoday API
68 stars 15 forks source link

SSO Authentication flow #7

Open elisaado opened 4 years ago

elisaado commented 4 years ago

SOMToday has introduced a new way of authentication, and there are already several organisations that have enabled it and disabled username/password authentication. I found this out when #6 was opened.

I am afraid that it is not possible to retrieve tokens this way but I hope that there is still hope for us.

What I need:

I have created a gitter room for further informal discussion: https://gitter.im/somtoday-api-docs/community

ChielChiel commented 4 years ago

I have recently tried to use a proxy to analyse the data requests made. However the SOMToday app and website, at least on iOS, do not allow custom certificates, which makes it impossible to see all of the networks requests and responses.

Do you know a quick fix that might be of help?

elisaado commented 4 years ago

Yeah the app uses certificate pinning, I know how this can be bypassed on android but not on iOS, the search term you need on Google is "disable certificate pinning iOS".

Thanks for wanting to help though :)

ChielChiel commented 4 years ago

How would the refresh token look like? Because it might be that the SSO login just returns a refresh token, from which the somtoday fetches the acces token for example. Or is that a crazy thought? Because today I came this far in intercepting the network requests made in the login process:

After login, the login.microsoftonline.com does the following get request:

GET https://somtoday.nl/oidc?code=0.ASEAv2TsGHBKb0OpDES5ibWnWpu4KK264sBLou9F0vkGQ7khAIk.AQABAAIAAAAm-06blBE1TpVMil8KPQ41w9B6RzkC9WfFOcRnPO-ZEoud6Ma5rn2WD0b4M8V-64aKic_fNgLbgdN-gS1aGNzQRWcrVG9socY61R4_Ii3pJLSKgDCAEDI57ShCUpfvuvgYuATrtt76zFwRodj--34G1w5hQxWdOXHrncMEWFG5qpU5MY84t1p3ECcVrlhFq9p9vUQbvn09DeVgSpFy4UYtqzLWZE1wR4VrWNknrZUA11xMP7FPHU32DHpJCwhfubY8J0vbKrNAtQg5FEKBOwV806dpLg9Z-ETdt8Riy4_gTm8iYfPJHboAL-yvjT4cIclWAgXAH7RsC1Z73rSajCx8q08SZikcQkd2BK_M1qA2LgjvJQSY47SorgXCOtZmKzp0MyiFkwY6rQJBBpgHvTfHInSRh1sXb_ljrMtAH9uTLxSwcUVwKuZSu_6SP17VPNFv4e86ez89fbVLkXw_bKvuzdubcDiIFRgyU5tFsznTHTZERv5IP3OPMgRjsZddHMEZ21sM80tIHESwUEZm_SV6Ddlx3P7fBz6L0Ea3oG1AZwog8_Kb1xlTmdyYguGkp6mIBXta7WFEFiRlXRZDIKot4ByVWlP9ME6upVahYZUL6xyCE2XpUr3hO3i7X1jLj5iy1LKVUkB1NEOS-ve1aYuoQ42YdlzEPyjt8tDmUFGKSyg4dMIbJ2EQy1U-w5EvxswMmJvWjvULxYDVJbu-Vgcg2N8FdZ307WPmvT8wtBNC7iA9FmaLCttVGu4I-pkpHg7MKw2jSTKVVnVzXqiui-zPUc9bxvFlUNTvQdfxAthDFSAA&state=tZCQGq72SYkbjckPjvgAcZZYnYGOCpSnIeTBKQphzQQ&session_state=3f5b0921-93d3-47e7-8f77-d5fae175580c HTTP/1.1

Which gives the following response

HTTP/1.1 302 Found
Date: Fri, 12 Jun 2020 19:59:33 GMT
Server: Apache
Strict-Transport-Security: max-age=31536000; includeSubdomains
X-Frame-Options: SAMEORIGIN
Expires: Thu, 01 Jan 1970 00:00:00 GMT
Cache-Control: no-cache, no-store
Pragma: no-cache
X-Frame-Options: DENY
X-Frame-Options: DENY
Location: somtodayleerling://oauth:443/callback?code=eyJ6aXAiOiJERUYiLCJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiZGlyIn0..VdR_CESqKlkK4Xzw.BZyamfT4hehkFmX1_JLux0cJNTtRQUWPRI4OLLKX20ZlNL4WzLW5ym9C7xCPcWSQaq6rwoqsD23AWggb7jN2QrjLelGJmEsuEovVXL0LKls1ypVC2xt51KGficjWjvPMZDZsm1yjtbGb-NeuXlHvz8kb9N0j7kG2FiuJs5H2fAZwomsAWNpDLFu4FpqnLJhbuUi6mMaLh8BC1z7af_Cd1QXrHs4iWuFdeu6D17MWLcpVEO0jweWJn_c7t6B0D02u5y9uO9e-rHMxmXDdbt_Klhvw04rvPSOUnvOkSB0tytXj-X6JlOThp7fQMjjZ83AXD0WY0uSnBftH9Q_zZDhQ4ImcX3qEHyB_jJAK-xatdwpQmblVZ56VSgcrLXYAfilBo31hPLkzPo3Vx9J7kV9TSDhGIKoTIf9xqUulRr0XRBnrxOTpRDx29HDEguLvgW-uwn8T1MIEeh85_NG2rPqIRqVoh0LDEUAEozDIkNgNe_7wYCFblVfxuDdDKUpXj6xHzNIG6EefdCBNu7f2wsnY0bu6z6cZOke8M2Uj3lnXTg5t42kWVOGminvheKGYTw5Lkd8LN0UAqudd1UQRAVCT4t62zMwgj4poHe10KN243S9zLfcGRQzhXmwbcyv8Xn6w7dSRPIQIAt2X-ARByp-EzojfY3jnbEnYjOz-6ERsXMu1e7fLWUAMtCb22ZvkRnkGRUAg5K2K8dqgUkWQoCTu8QYDTztjBDzE5HFC_DAcIMVWgXvIp0VIyQLBeumdUB562u6e0LkSWegbuAVAz59hFwshIXEFLXNKNbDkM0Zy_LLer3YpHFpNxeNvQ1VEPuGEPWC6HftjI68M3HWJ0Bh0vaaSzTJ5Ny6lp1xhrKCLYuFAHNX5FMktIembMNVyv_8Sm9K2oXhdvkClNE0LnEJNN9c54FIVFxDEEzh03D-tiqaYucAOjrdDZ20x0NnNGaBDflj3HSWOuGsCh_cz44xX3WbyCJiHWQ_LgJ6JrPD8-WXnLsH-iXBwuOqsRcFf29lC.AKx_1wrE1iOccXhutWeywQ
Content-Length: 0
Set-Cookie: authentication=; Path=/oidc; Max-Age=0; Expires=Thu, 01-Jan-1970 00:00:00 GMT
X-Robots-Tag: none
X-UA-Compatible: IE=Edge
Keep-Alive: timeout=2, max=100
Connection: Keep-Alive

With somtodayleerling:// presumably being the target url of the app.

elisaado commented 4 years ago

That's very interesting, it could be the access token, a refresh token or an ID token. Was this request made with the app or with the browser?

ChielChiel commented 4 years ago

Well, at least on iOS, in the app, it opens a safari webview in which the entire login process takes place. From this webview the somtodayleerling:// request is made.

elisaado commented 4 years ago

I see, I have found a new grant_type called authorization_code which is linked to this!

elisaado commented 4 years ago

When I try to use your code I get:

{
    "error_description": "Invalid grant: Token has expired",
    "error": "invalid_grant"
}

So I think the code you mentioned can actually be used to fetch an access_token

elisaado commented 4 years ago

I get the same request made on my android when I try to use our school's new SSO function, I will try to throttle the connection so I get to use the code myself.

elisaado commented 4 years ago

I think it will still be possible to authenticate even with SSO, but it will be very ugly (saving Set-Cookie headers, etc)

elisaado commented 4 years ago

The tenant cookie is just base64 of the school uuid :) We only need to save JSESSIONID :)

ChielChiel commented 4 years ago

Wow great to see all these updates! Good work!

elisaado commented 4 years ago

This is the problem I'm having: everything is different for every school the microsoft oauth service works very differently from the idpcluster of stichtingcarmelcollege

There is no easy way to fix this issue

ChielChiel commented 4 years ago

Right, i see. Btw wjat exactly do you mean with “idpcluster”?

Secondly, can’t we let the admin, who will use these api to log users in, specify some of the username and password field names, so you can post that data easily?

Not sure if possible, but most request libraries allow you to follow redirects and post data on the way.

ChielChiel commented 4 years ago

Btw, decrypting any string starting with "eyJ6aXAiOiJERUYiLCJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiZGlyIn0" or something like this is just a base64 encode of {"zip":"DEF","cty":"JWT","enc":"A256GCM","alg":"dir"}. JWT is apparently a Json Web Token. And this: Authorization: Basic RDUwRTBDMDYtMzJEMS00QjQxLUExMzctQTlBODUwQzg5MkMyOnZEZFdkS3dQTmFQQ3loQ0RoYUNuTmV5ZHlMeFNHTkpY could be a key to decrypt that the rest of the string.

Maybe that string holds all of the encrypted content like username, access token and whatever. However I haven't been able to decrypt A256GCM.

And curl "https://production.somtoday.nl/oauth2/token" -d "grant_type=refresh_token&refresh_token=[token] gives the following response: {"error_description":"Invalid request: Unexpected number of Base64URL parts, must be three","error":"invalid_request"}.

However, notice the .. in Location: somtodayleerling://oauth:443/callback?code=eyJ6aXAiOiJERUYiLCJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiZGlyIn0..VdR_CESqKlkK4Xzw.BZyamfT4hehkFmX1_JLux0cJNTtRQUWPRI4OLLKX20ZlNL4WzLW5ym9C7xCPcWSQaq6rwoqsD23AWggb7jN2QrjLelGJmEsuEovVXL0LKls1ypVC2xt51KGficjWjvPMZDZsm1yjtbGb-NeuXlHvz8kb9N0j7kG2FiuJs5H2fAZwomsAWNpDLFu4FpqnLJhbuUi6mMaLh8BC1z7af_Cd1QXrHs4iWuFdeu6D17MWLcpVEO0jweWJn_c7t6B0D02u5y9uO9e-rHMxmXDdbt_Klhvw04rvPSOUnvOkSB0tytXj-X6JlOThp7fQMjjZ83AXD0WY0uSnBftH9Q_zZDhQ4ImcX3qEHyB_jJAK-xatdwpQmblVZ56VSgcrLXYAfilBo31hPLkzPo3Vx9J7kV9TSDhGIKoTIf9xqUulRr0XRBnrxOTpRDx29HDEguLvgW-uwn8T1MIEeh85_NG2rPqIRqVoh0LDEUAEozDIkNgNe_7wYCFblVfxuDdDKUpXj6xHzNIG6EefdCBNu7f2wsnY0bu6z6cZOke8M2Uj3lnXTg5t42kWVOGminvheKGYTw5Lkd8LN0UAqudd1UQRAVCT4t62zMwgj4poHe10KN243S9zLfcGRQzhXmwbcyv8Xn6w7dSRPIQIAt2X-ARByp-EzojfY3jnbEnYjOz-6ERsXMu1e7fLWUAMtCb22ZvkRnkGRUAg5K2K8dqgUkWQoCTu8QYDTztjBDzE5HFC_DAcIMVWgXvIp0VIyQLBeumdUB562u6e0LkSWegbuAVAz59hFwshIXEFLXNKNbDkM0Zy_LLer3YpHFpNxeNvQ1VEPuGEPWC6HftjI68M3HWJ0Bh0vaaSzTJ5Ny6lp1xhrKCLYuFAHNX5FMktIembMNVyv_8Sm9K2oXhdvkClNE0LnEJNN9c54FIVFxDEEzh03D-tiqaYucAOjrdDZ20x0NnNGaBDflj3HSWOuGsCh_cz44xX3WbyCJiHWQ_LgJ6JrPD8-WXnLsH-iXBwuOqsRcFf29lC.AKx_1wrE1iOccXhutWeywQ

When I remove one . it gives the following response {"error_description":"Invalid request: Invalid serialized JWE object: Missing fourth delimiter","error":"invalid_request"}`

elisaado commented 4 years ago

I'd like you to answer my question in #6

elisaado commented 4 years ago

The token you've found (somtodayleerling://oauth:443/callback?code=eyJ6aXAiOiJERUYiLCJjdHkiOiJKV1QiLCJlbmMiOiJBMjU2R0NNIiwiYWxnIjoiZGlyIn0..VdR_CESqKlkK4Xzw.BZyamfT4hehkFmX1_JLux0cJNTtRQUWPRI4OLLKX20ZlNL4WzLW5ym9C7xCPcWSQaq6rwoqsD23AWggb7jN2QrjLelGJmEsuEovVXL0LKls1ypVC2xt51KGficjWjvPMZDZsm1yjtbGb-NeuXlHvz8kb9N0j7kG2FiuJs5H2fAZwomsAWNpDLFu4FpqnLJhbuUi6mMaLh8BC1z7af_Cd1QXrHs4iWuFdeu6D17MWLcpVEO0jweWJn_c7t6B0D02u5y9uO9e-rHMxmXDdbt_Klhvw04rvPSOUnvOkSB0tytXj-X6JlOThp7fQMjjZ83AXD0WY0uSnBftH9Q_zZDhQ4ImcX3qEHyB_jJAK-xatdwpQmblVZ56VSgcrLXYAfilBo31hPLkzPo3Vx9J7kV9TSDhGIKoTIf9xqUulRr0XRBnrxOTpRDx29HDEguLvgW-uwn8T1MIEeh85_NG2rPqIRqVoh0LDEUAEozDIkNgNe_7wYCFblVfxuDdDKUpXj6xHzNIG6EefdCBNu7f2wsnY0bu6z6cZOke8M2Uj3lnXTg5t42kWVOGminvheKGYTw5Lkd8LN0UAqudd1UQRAVCT4t62zMwgj4poHe10KN243S9zLfcGRQzhXmwbcyv8Xn6w7dSRPIQIAt2X-ARByp-EzojfY3jnbEnYjOz-6ERsXMu1e7fLWUAMtCb22ZvkRnkGRUAg5K2K8dqgUkWQoCTu8QYDTztjBDzE5HFC_DAcIMVWgXvIp0VIyQLBeumdUB562u6e0LkSWegbuAVAz59hFwshIXEFLXNKNbDkM0Zy_LLer3YpHFpNxeNvQ1VEPuGEPWC6HftjI68M3HWJ0Bh0vaaSzTJ5Ny6lp1xhrKCLYuFAHNX5FMktIembMNVyv_8Sm9K2oXhdvkClNE0LnEJNN9c54FIVFxDEEzh03D-tiqaYucAOjrdDZ20x0NnNGaBDflj3HSWOuGsCh_cz44xX3WbyCJiHWQ_LgJ6JrPD8-WXnLsH-iXBwuOqsRcFf29lC.AKx_1wrE1iOccXhutWeywQ)

can be used to fetch an access token, by calling

https://production.somtoday.nl/oauth2/token with the following x-www-form-urlencoded body:

grant_type:authorization_code
code:<the code you got from somtodayleerling://oauth:443/callback?>
//scope:openid
client_id:D50E0C06-32D1-4B41-A137-A9A850C892C2
redirect_uri:somtodayleerling://oauth/callback
code_verifier:<some random string, I think>
ChielChiel commented 4 years ago

Aight, well, when I run the following command I get {"error_description":"Invalid grant: Token has expired","error":"invalid_grant"}

I even tried getting a new key by going here: https://somtoday.nl/oauth2/authorize?response_type=code&client_id=D50E0C06-32D1-4B41-A137-A9A850C892C2&redirect_uri=somtodayleerling://oauth/callback&scope=openid&tenant_uuid=cbeab658-c9e6-4a54-b419-8f682f82ea54&prompt=login&session=no_session&code_challenge=uLYUyUVgId8zjbxN4oLNCMa-DzcF_5nRnFOkZensf_w&code_challenge_method=S256 Which is the webpage that is opened when you login with sso in the somtoday app

curl -v "https://production.somtoday.nl/oauth2/token" -d "grant_type=authorization_code&code=$code&scope=openid&client_id=D50E0C06-32D1-4B41-A137-A9A850C892C2&redirect_uri=somtodayleerling://oauth/callback&code_verifier=cbeab658-c9e6-4a54-b419-8f682f82ea54\\87c63403-49c9-48db-b5a4-458f515a2695"
elisaado commented 4 years ago

Yeah you need to authorize with a password first and also supply the cookies

elisaado commented 4 years ago

I am unable to get it working consistently, and I have no clue how I would handle different authentication providers.

Could you provide a proxy or network log of your full log-in with the credentials and tokens blanked out?

ChielChiel commented 4 years ago

I have tried to login with an extensive curl php script. However after login at my schools login system it would just create a loop of login screens, strange. In what kind of format would you like to see all those logs?

christosk92 commented 4 years ago

Are you guys still actively working on it? I still have my SOM Credentials but SOM is down for the weekend now. When it's back up I could go ahead and try if I can get authentication to work again.

christosk92 commented 4 years ago

PS: Back in the day, SOM just stored their auth credentials in their app, which you could just easily decompile for the Android app.

ChielChiel commented 4 years ago

Hi, not actively anymore as I have just graduated. However, if you could find a way to automate the SSO authentication flow it would be a Great Leap Forward!

elisaado commented 4 years ago

Hey guys,

@christosk92 Any help would be greatly appreciated, what I have in mind for now is the following flow: any implementation library of this documentation would do something along the lines of

it's all standard oauth flow until the "user copies url" part because I have found no way to alter the redirect_uri (I think?.. it's been a while since I've worked on this).

elisaado commented 4 years ago

@ChielChiel sorry for not replying to the comment you made on the 18th of June, I was kind of busy (toetsweek) and then forgot about it.

christosk92 commented 4 years ago

Do you guys still need a SOM account? Mine still works

elisaado commented 4 years ago

Yes, that'd definitely help

christosk92 commented 4 years ago

I've sent them to your mail linked to your GitHub

elisaado commented 4 years ago

Thank you! I'll continue the work when somtoday is back up

elisaado commented 4 years ago

I wonder what kind of maintenance would require them to go offline for 6 days :thinking:

christosk92 commented 4 years ago

Don't they do this every summer? I think they're cleaning up their DB and making space for new users, updating api's etc

elisaado commented 4 years ago

I don't know, to be honest

elisaado commented 4 years ago

My old school used magister, this is my second year using somtoday

elisaado commented 4 years ago

I have created a gitter room for further informal discussion: https://gitter.im/somtoday-api-docs/community

christosk92 commented 4 years ago

I prefer Discord as I find gitter pretty annoying but sure

elisaado commented 4 years ago

In that case I could make a discord server, as I also already have another contributor on discord

christosk92 commented 4 years ago

Som pushed the end of the maintenance back to 3th of august.. image

elisaado commented 4 years ago

what the FUCK are they doing

christosk92 commented 4 years ago

Som is back up 🎉🎉

elisaado commented 4 years ago

Screenshot_20200803-163547

I like the new style! no grades no schedule it's so much better

elisaado commented 4 years ago

something happened in the new app, it seems that every school now uses oauth2 authentication (the provider being somtoday or a single sign on provider)

elisaado commented 4 years ago

I am still able to login by username and password via the REST API (without oauth) but I don't know how long until I can't do so anymore

elisaado commented 4 years ago

It seems that adding the following header:

Referer: android-app://nl.topicus.somtoday.leerling/

and adding a hook to make the webview stop when https://somtoday.nl/login?0-2.-passwordForm&loginLink=x responds yields a callback oauth code. (in the location header of the response)

this code can be exchanged somewhere

elisaado commented 4 years ago

Okay, the SSO providers for a school can be found at https://servers.somtoday.nl/organisaties.json (oidcurls)

When there is more than one object in the oidcurls array, the official app lets the user select which SSO provider to use.

Then, a webview is opened with the following URL: https://somtoday.nl/oauth2/authorize?redirect_uri=somtodayleerling%3A%2F%2Foauth%2Fcallback&client_id=D50E0C06-32D1-4B41-A137-A9A850C892C2&response_type=code&prompt=login&state=UNlYiXONB69K8uNwNJ2rCw&scope=openid&code_challenge=-OyuBNyqCP6pFRjotemGVkq9WX0hGhacBOFZsPEu-o8&code_challenge_method=S256&tenant_uuid=4213a402-b898-4d16-9ebb-8c5f02b57474&oidc_iss=https%3A%2F%2Fidpcluster.stichtingcarmelcollege.nl%2Fnidp%2Foauth%2Fnam&session=no_session

Pretty:

https://somtoday.nl/oauth2/authorize

redirect_uri          = somtodayleerling://oauth/callback
client_id             = D50E0C06-32D1-4B41-A137-A9A850C892C2
response_type         = code
prompt                = login
state                 = UNlYiXONB69K8uNwNJ2rCw
scope                 = openid
code_challenge        = -OyuBNyqCP6pFRjotemGVkq9WX0hGhacBOFZsPEu-o8
code_challenge_method = S256
tenant_uuid           = 4213a402-b898-4d16-9ebb-8c5f02b57474
oidc_iss              = https://idpcluster.stichtingcarmelcollege.nl/nidp/oauth/nam
session               = no_session

where tenant_uuid is the uuid of the organisation oidc_iss is the selected SSO provider state is a random string that should be stored for further requests to validate the response's state code_challenge is a url-safe base64 sha256 random secret. Store the secret as you will need it to later exchange the code for a token pair.

everything else is a constant

elisaado commented 3 years ago

Everything seems to work, updating the documentation soon.

berylllium commented 3 years ago

When would that be? :)

elisaado commented 3 years ago

soon™️

nah, just gotta wait for my test week to finish and then for me to gather enough motiviation

berylllium commented 3 years ago

Understandable. Good luck with your test week, I'm sure we'll both need it.

elisaado commented 3 years ago

Thank you, and good luck to you as well haha

Fish-o commented 3 years ago

maybe close this?