-
Notifications
You must be signed in to change notification settings - Fork 4
BrightHorizons Login Failure: 405 HTTP Verb Not Allowed Error on POST Request #48
Comments
it looks like BrightHorizons has changed their login flow - the POST to the url in the code is no longer a valid way to authenticate. I will see if I can figure out the new flow. |
Happy to assist in the debugging if there is anything I can help with. On 19. Jan 2024, at 13:07, Leo Covarrubias ***@***.***> wrote:
it looks like BrightHorizons has changed their login flow - the POST to the url in the code is no longer a valid way to authenticate. I will see if I can figure out the new flow.
—Reply to this email directly, view it on GitHub, or unsubscribe.You are receiving this because you authored the thread.Message ID: ***@***.***>
|
when I try to visit: the login form at this new url (as far as i can tell not being able to actually log in) posts to itself with a more complex request including a ephemeral verification token like so:
not sure how we would be able to generate the |
in the repository the file |
Indeed, it seems the login process of BH involves multiple steps, including redirections. From googling, the __RequestVerificationToken is an anti-forgery token used to prevent CSRF attacks, and it is usually generated by the server and must be included in a subsequent POST request. The From what i can see, when we visit https://familyinfocenter.brighthorizons.com/mybrightday/login, we are redirected to a centralized login system at https://bhlogin.brighthorizons.com. This system handles authentication for various Bright Horizons services and requires a benefitId and fsTargetId to route the login process correctly. Upon submitting my login credentials, a POST request is made to https://bhlogin.brighthorizons.com, which includes The browser is directed back to the Family Information Center https://familyinfocenter.brighthorizons.com/welcome/login, maybe indicating the completion of the authentication process. As I see in the request named
Let me know what i can check next. |
the
at this point on being redirected to https://familyinfocenter.brighthorizons.com/welcome/login either the new cookies are used for api request validation or there is some javascript that fetches additional data to eventually get an api key. you can check a browsers dev tools once fully logged in and inspect the requests that fetch data to see what is authenticating the api - either the previous |
It's going to be very difficult for me to reverse engineer the login flow as it stands. If you are comfortable with Python I've started a script attempting to execute the login flow in this branch: executed as: $ python api_docs/bright_horizons_login_flow.py <username> <password> Perhaps you are able to make progress with this? As a side note it seems that 4 invalid login attempts triggers an account lock (seems they probably send an email to unlock). Important indicators you can reference after login in Chrome dev tools network capture under
|
I got this working today - after the CSRF hurdle, we also need to go through a SAML request, exchange the Bright Horizons token for a Tadpole token, and then we can exchange that for session cookies. Once those are available, I'm able to hit endpoints like class Client
BRIGHT_HORIZONS_BASE = 'https://bhlogin.brighthorizons.com'
APIS = {
bright_horizons: 'https://mbdwgateway.brighthorizons.com/api',
tadpole: 'https://mybrightday.brighthorizons.com'
}
DEBUG = false
attr_reader :username, :password, :bh_token, :tadpole_token, :tadpole_cookies
def initialize(username, password)
@username = username
@password = password
end
def debug(*strs)
puts(*strs) if DEBUG
end
def log_in
bh_start_page = HTTP.get("#{BRIGHT_HORIZONS_BASE}/?benefitid=5&fstargetid=1")
verification_token = Nokogiri::HTML(bh_start_page.body.to_s).css('input[name="__RequestVerificationToken"]').first['value']
cookies = bh_start_page.cookies
login_response = HTTP.cookies(cookies).post(
"#{BRIGHT_HORIZONS_BASE}",
form: {
username: username,
password: password,
__RequestVerificationToken: verification_token,
benefitid: 5,
fstargetid: 1,
userType: 0
}
)
cookies = login_response.cookies
redirect = login_response.headers['Location']
bh_response = HTTP.cookies(cookies).get("#{BRIGHT_HORIZONS_BASE}#{redirect}")
html = Nokogiri::HTML(bh_response.body.to_s)
action = html.css('form').first['action']
saml_response = html.css('input[name="SAMLResponse"]').first['value']
finish_saml = HTTP.cookies(cookies).post(action, form: { SAMLResponse: saml_response })
cookies = finish_saml.cookies
@bh_token = cookies.find { |c| c.name == 'acs' }.value
token_response = HTTP.headers({ Authorization: "Bearer #{@bh_token}" }).get("#{APIS[:bright_horizons]}/account/token2")
@tadpole_token = token_response.parse(:json)['token']
tadpole_login = HTTP.get("#{APIS[:tadpole]}/auth/jwt/redirect", params: { jwt: @tadpole_token })
@tadpole_cookies = tadpole_login.cookies
nil
end
def children
@children ||= get(:bright_horizons, '/home/mychildren', {})['children']
end
def events(date)
debug "Fetching events from #{date.to_time.utc.to_i} to #{(date + 1).to_time.utc.to_i}"
page_size = 1000
data = get(
:tadpole,
'/remote/v1/events',
client: 'dashboard',
direction: 'range',
num_events: page_size, # They send this param but don't appear to use it
earliest_event_time: date.to_time.utc.to_i,
latest_event_time: (date + 1).to_time.utc.to_i
)
all_events = data['events']
debug "Found #{all_events.size} events"
all_events
end
def get(api, path, params)
token, cookies = api == :bright_horizons ? [bh_token, {}] : [tadpole_token, tadpole_cookies]
response = HTTP.cookies(cookies)
.get("#{APIS[api]}#{path}",
headers: { Authorization: "Bearer #{token}" },
params: params)
response.parse(:json)
end
end |
Thank you @kristjan for figuring this out and providing an example! It should be enough for me to update the login flow and get a branch out for testing. |
@kristjan after you get a tadpoles-token from calling POST https://mybrightday.brighthorizons.com/api/v2/auth/jwt/validate
Content-Type: application/x-www-form-urlencoded
token=<tadpoles-token> to get a bh-api-key and then call the v2 api endpoint such as: GET https://mybrightday.brighthorizons.com/api/v2/user/profile
Accept: application/json
X-Api-Key: <bh-api-key> I'd like to figure out how much modification i'll need to do besides the login flow. |
I think they've replaced Other |
I updated the branch: bright-horizons-new-login-flow / PR with changes to start to bring things in line with @kristjan ruby example. I have no way to test this since I don't have a BH account so unless someone steps up to push this forward I think this is stuck. The contents of I started writing some tests for parts that parse HTML here: |
I tested it, and I am getting the following error: |
More info:
Even if the username/password is random characters |
I debugged a little bit, and I think the problem is with the cookies. |
Describe the bug
When attempting to use the tadpoles-backup tool with the stat command to log into Bright Horizons, the process fails with a "405 - HTTP verb used to access this page is not allowed" error. This occurs during the login attempt to Bright Horizons, suggesting an issue with the HTTP method used for the POST request to /mybrightday/login.
Note that I used the same username (not email) and password I used to login to: https://familyinfocenter.brighthorizons.com/mybrightday/login successfully.
To Reproduce
Steps to reproduce the behavior:
Expected behavior
I expected the tadpoles-backup tool to successfully log into the Bright Horizons service and provide status information without encountering a server error.
Logs
Logs from the console:
System Info (please complete the following information):
I used the latest
tadpoles-backup-darwin-amd64.zip
build from release v2.1.0The text was updated successfully, but these errors were encountered: