Module: Authenticator

Extended by:
Authenticator
Included in:
Authenticator
Defined in:
lib/magister/authenticator.rb

Overview

A module used to authenticate a user with the magister api.

Instance Method Summary collapse

Instance Method Details

#login(username, password, school) ⇒ Object

Log in with username and password

Parameters:

  • username (String)

    The username, usually in the form of a “leerlingnummer”

  • password (String)

    The users password

  • school (String)

    The school the user attends

Since:

  • 1.1.0



22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
# File 'lib/magister/authenticator.rb', line 22

def (username, password, school)
    # uri = URI("https://#{school}.magister.net/oidc_config.js")
    # http = Net::HTTP.new(uri.host, uri.port)
    # http.use_ssl = true
    # request = Net::HTTP::Get.new(uri.request_uri)
    # response = http.request(request)
    # oidc_conf = (response.body.split("config =").last.split("};").first.gsub("window.location.hostname", "'" + uri.hostname + "'") + "}").gsub(': ', '":').gsub(/,(\s*)/, ',"').gsub(/{(\s*)/, '{"').gsub("'", '"').gsub('" + "', "")

    # oidc_conf = JSON.parse(oidc_conf)
    if $magister_useCache && File.exist?($magister_cachingDirectory + "/auth.json")
        f = File.open($magister_cachingDirectory + "/auth.json")
        cached_data = f.read
        cached_data = JSON.parse(cached_data)
        expires = Time.at(cached_data["expires"].to_i)
        puts "attempting to use cached token..."
        if expires.to_i > Time.now.to_i
          puts "using cached token."
          return Profile.new(cached_data["token"], school)
        else
          puts "cached token expired."
        end
    end

    codeVerifier  = SecureRandom.alphanumeric(128)
    verifier      = Base64.urlsafe_encode64(codeVerifier)

    rawChallenge = Digest::SHA256.hexdigest verifier
    challenge = Base64.urlsafe_encode64(rawChallenge)

    @@state = SecureRandom.hex(16)
    @@nonce = SecureRandom.hex(16)

    auth_uri = "https://#{school}.magister.net/connect/authorize?client_id=M6LOAPP&redirect_uri=m6loapp%3A%2F%2Foauth2redirect%2F&scope=openid%20profile%20opp.read%20opp.manage%20attendance.overview%20attendance.administration%20calendar.ical.user%20calendar.to-do.user%20grades.read%20grades.manage&state=#{@@state}&nonce=#{@@nonce}&code_challenge=#{challenge}&code_challenge_method=S256&prompt=select_account"
    # puts "using authentication url #{auth_uri}"

    token = ""
    if $authMode == "local"
        raise NotImplementedError.new("\n\nLocal authentication mode has not been implemented yet, \nCheck our github for any updates, or if you want to help implementing it!\n")
    else
        if $magister_browser == "Chrome"
            puts "Using Selenium with Chrome for authentication."
            if !$magister_disableHeadless
                options = Selenium::WebDriver::Options.chrome(args: ['--headless=new'])
            else
                options = Selenium::WebDriver::Options.chrome(args: [])
            end
            driver = Selenium::WebDriver.for :chrome, options: options
        elsif $magister_browser == "Firefox"
            puts "Using Selenium with Firefox for authentication."
            if !$magister_disableHeadless
                options = Selenium::WebDriver::Options.firefox(args: ['--headless=new'])
            else
                options = Selenium::WebDriver::Options.firefox(args: [])
            end
            driver = Selenium::WebDriver.for :firefox, options: options
        else
            puts "Invalid browser option #{$magister_browser}"
            return
        end

        driver.get auth_uri
        while !driver.current_url.start_with? "https://accounts.magister.net/account/login"
            sleep(0.5)
            # puts "waiting for load..."
        end
        sleep(3)

        username_field = driver.find_element(id: 'username')
        username_field.send_keys(username)
        go_to_password_button = driver.find_element(id: 'username_submit')
        go_to_password_button.click

        sleep(1)

        password_field = driver.find_element(id: 'password')
        password_field.send_keys(password)
         = driver.find_element(id: 'password_submit')
        .click

        wait = Selenium::WebDriver::Wait.new(timeout: 30)
        wait.until { driver.current_url.start_with? "https://#{school}.magister.net/oidc/redirect_callback.html" }

        expires_in = driver.current_url.split("expires_in=").last.split("&").first.to_i
        expires = Time.now + expires_in
        token = driver.current_url.split("access_token=").last.split("&").first

        driver.quit
    end

    if $magister_useCache
        if $magister_cacheType == "json"
          File.write($magister_cachingDirectory + "/auth.json", "{\"token\": \"#{token}\", \"expires\": \"#{expires.to_i}\"}")
        end
    end

    return Profile.new(token, school)
end