Class: RackOidcApi::Middleware

Inherits:
Object
  • Object
show all
Defined in:
lib/rack-oidc-api/middleware.rb

Instance Method Summary collapse

Constructor Details

#initialize(app, opts) ⇒ Middleware

Returns a new instance of Middleware.



18
19
20
21
22
23
24
25
26
27
28
29
30
# File 'lib/rack-oidc-api/middleware.rb', line 18

def initialize(app, opts)
    @app = app

    raise "provider must be specified" if !opts[:provider]
    raise "audience must be specified" if !opts[:audience]

    @provider = opts[:provider].gsub(/\/\z/, '')
    @audience = opts[:audience]
    @pinned_cert = opts[:pinned_provider_ssl]
    @lock = Mutex.new

    reload_options
end

Instance Method Details

#call(env) ⇒ Object



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
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
# File 'lib/rack-oidc-api/middleware.rb', line 94

def call(env)
    header = env['HTTP_AUTHORIZATION']
    if !header
        return mkerror("Missing Authorization Header")
    end

    if !header.match(BEARER_TOKEN_REGEX)
        return mkerror("Invalid Bearer token")
    end

    _, token = header.split(/\s/, 2)

    check_reload
    if !@avail
        return mkerror("OIDC provider unavailable")
    end

    jwk_loader = proc do |options|
        @jwks
    end

    begin
        jwt = JWT.decode(token, nil, true, {
            algorithms: @algorithms,
            jwks: jwk_loader,
            aud: @audience,
            verify_aud: true,
            nbf_leeway: 30,
            exp_leeway: 30
        })
        env[:identity_token] = jwt
    rescue JWT::JWKError => e
        # Handle problems with the provided JWKs
        return mkerror("Invalid Bearer token")
    rescue JWT::DecodeError => e
        # Handle other decode related issues e.g. no kid in header, no matching public key found etc. 
        return mkerror(e.message)
    end

    @app.call(env)
end

#check_reloadObject



82
83
84
85
86
87
88
89
90
91
92
# File 'lib/rack-oidc-api/middleware.rb', line 82

def check_reload
    locked = @lock.try_lock
    return unless locked # Only have one reload checking thread at once
    begin
        if @valid < Time.now
            reload_options
        end
    ensure
        @lock.unlock
    end
end

#reload_optionsObject



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
# File 'lib/rack-oidc-api/middleware.rb', line 32

def reload_options
    begin
        oidc_config_uri = URI("#{@provider}/.well-known/openid-configuration")
        oidc_config_raw = http_get(oidc_config_uri)
        raise "Failed to retrieve OIDC Discovery Data" unless oidc_config_raw
        oidc_config = JSON.parse(oidc_config_raw)
        raise "Invalid or missing OIDC Discovery Data" unless oidc_config

        jwks_uri = oidc_config['jwks_uri']
        raise "No JWKS URI in OIDC Discovery" unless jwks_uri

        # Do not allow JWKS from a different origin (scheme, host, port)
        jwks_uri = URI(jwks_uri)
        jwks_uri.scheme = oidc_config_uri.scheme
        jwks_uri.host = oidc_config_uri.host
        jwks_uri.port = oidc_config_uri.port

        jwks_raw = http_get(jwks_uri)
        raise "Failed to retrieve JWKS File" unless jwks_raw
        
        jwks = JSON.parse(jwks_raw)
        algorithms = ALGORITHMS - (ALGORITHMS - oidc_config['id_token_signing_alg_values_supported'] || [])

        keys = []
        jwks['keys'].each do |key|
            rec = {}
            key.each do |k, v|
                rec[k.to_sym] = v
            end
            keys << rec
        end

        @jwks = {keys: keys}
        @algorithms = algorithms
        @valid = Time.now + 300
        @avail = true
    rescue JSON::JSONError
        @avail = false
        @valid = Time.now + 60
    rescue StandardError => e
        STDERR.puts(e.message)
        @avail = false
        @valid = Time.now + 60
    rescue URI::InvalidURIError => e
        STDERR.puts(e.message)
        @avail = false
        @valid = Time.now + 60
    end
end