omniauth-doordash-oauth2 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 250a2fe65c8b77872dd07f6efc706f4205e21b585f7544b754e0aa725a8324b6
4
+ data.tar.gz: 639c028411988132f1ddf50d2ade83751eb7c73aa4f559f6ff899d3f0c044037
5
+ SHA512:
6
+ metadata.gz: 0f0d8036694cea5cc1f21e69f2b631e8af197f15d3829465071ac31450f4336463510024bbaee519d28b7af13ef4434f6df46ba0d99aa53399ea77b991fd8131
7
+ data.tar.gz: a9bbea25db14467b707a861c978ed3850e61297dba10086ab2db340f4f801e7584c7b2ea3e3779c818bf720f520b64507d4d2224eaf45fb35a92d86e372723af
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ ## [0.1.0] - 2026-03-16
4
+
5
+ ### Added
6
+
7
+ - Initial release
8
+ - JWT-based authentication strategy for DoorDash (HMAC-SHA256)
9
+ - Automatic business info fetching from `/developer/v1/businesses`
10
+ - Base64url signing secret decoding with automatic padding
11
+ - Fallback to `developer_id` when API is unavailable
12
+ - 100% line and branch test coverage
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 dan1d
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,138 @@
1
+ # OmniAuth DoorDash OAuth2
2
+
3
+ [![Gem Version](https://badge.fury.io/rb/omniauth-doordash-oauth2.svg)](https://badge.fury.io/rb/omniauth-doordash-oauth2)
4
+ [![CI](https://github.com/dan1d/omniauth-doordash-oauth2/actions/workflows/ci.yml/badge.svg)](https://github.com/dan1d/omniauth-doordash-oauth2/actions/workflows/ci.yml)
5
+ [![Coverage: 100%](https://img.shields.io/badge/coverage-100%25-brightgreen)](https://github.com/dan1d/omniauth-doordash-oauth2)
6
+ [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)
7
+
8
+ An [OmniAuth](https://github.com/omniauth/omniauth) strategy for authenticating with [DoorDash](https://developer.doordash.com/) using JWT-based authentication.
9
+
10
+ **Note:** DoorDash does NOT use standard OAuth2 flows. Instead, each API request is signed with a JWT using HMAC-SHA256. This gem implements `OmniAuth::Strategy` directly (not `OmniAuth::Strategies::OAuth2`).
11
+
12
+ ## Installation
13
+
14
+ Add this line to your application's Gemfile:
15
+
16
+ ```ruby
17
+ gem 'omniauth-doordash-oauth2'
18
+ ```
19
+
20
+ And then execute:
21
+
22
+ ```bash
23
+ bundle install
24
+ ```
25
+
26
+ Or install it yourself as:
27
+
28
+ ```bash
29
+ gem install omniauth-doordash-oauth2
30
+ ```
31
+
32
+ ## Usage
33
+
34
+ ### Rails with Devise
35
+
36
+ In your `config/initializers/devise.rb`:
37
+
38
+ ```ruby
39
+ config.omniauth :doordash_oauth2,
40
+ developer_id: ENV["DOORDASH_DEVELOPER_ID"],
41
+ key_id: ENV["DOORDASH_KEY_ID"],
42
+ signing_secret: ENV["DOORDASH_SIGNING_SECRET"]
43
+ ```
44
+
45
+ ### Standalone OmniAuth
46
+
47
+ In your Rack middleware configuration:
48
+
49
+ ```ruby
50
+ use OmniAuth::Builder do
51
+ provider :doordash_oauth2,
52
+ developer_id: ENV["DOORDASH_DEVELOPER_ID"],
53
+ key_id: ENV["DOORDASH_KEY_ID"],
54
+ signing_secret: ENV["DOORDASH_SIGNING_SECRET"]
55
+ end
56
+ ```
57
+
58
+ ## Auth Hash Schema
59
+
60
+ The auth hash returned by this strategy has the following structure:
61
+
62
+ ```ruby
63
+ {
64
+ provider: "doordash_oauth2",
65
+ uid: "external_business_id", # Falls back to developer_id if API unavailable
66
+ info: {
67
+ name: "Store Name",
68
+ business_name: "Store Name",
69
+ email: nil # DoorDash does not provide merchant email
70
+ },
71
+ extra: {
72
+ raw_info: {
73
+ "store_id" => "external_business_id",
74
+ "store_name" => "Store Name",
75
+ "id" => "external_business_id"
76
+ }
77
+ }
78
+ }
79
+ ```
80
+
81
+ ## JWT Authentication Details
82
+
83
+ DoorDash uses JWT-based authentication instead of OAuth2. Each API request includes a signed JWT in the `Authorization` header.
84
+
85
+ ### JWT Header
86
+
87
+ ```json
88
+ {
89
+ "alg": "HS256",
90
+ "dd-ver": "DD-JWT-V1"
91
+ }
92
+ ```
93
+
94
+ ### JWT Payload
95
+
96
+ ```json
97
+ {
98
+ "aud": "doordash",
99
+ "iss": "<developer_id>",
100
+ "kid": "<key_id>",
101
+ "exp": "<now + 300>",
102
+ "iat": "<now>"
103
+ }
104
+ ```
105
+
106
+ The signing secret is base64url-encoded. The gem automatically decodes it (adding padding if needed) before signing.
107
+
108
+ ## Configuration Options
109
+
110
+ | Option | Required | Default | Description |
111
+ |--------|----------|---------|-------------|
112
+ | `developer_id` | Yes | `nil` | Your DoorDash developer ID |
113
+ | `key_id` | Yes | `nil` | Your DoorDash key ID |
114
+ | `signing_secret` | Yes | `nil` | Your DoorDash signing secret (base64url-encoded) |
115
+ | `api_base_url` | No | `https://openapi.doordash.com` | DoorDash API base URL |
116
+
117
+ ## Development
118
+
119
+ After checking out the repo, run `bundle install` to install dependencies.
120
+
121
+ ```bash
122
+ # Run tests
123
+ bundle exec rspec
124
+
125
+ # Run linter
126
+ bundle exec rubocop
127
+
128
+ # Run both (default rake task)
129
+ bundle exec rake
130
+ ```
131
+
132
+ ## Contributing
133
+
134
+ Bug reports and pull requests are welcome on GitHub at https://github.com/dan1d/omniauth-doordash-oauth2.
135
+
136
+ ## License
137
+
138
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ module OmniAuth
4
+ module DoordashOauth2
5
+ VERSION = '0.1.0'
6
+ end
7
+ end
@@ -0,0 +1,140 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'omniauth'
4
+ require 'jwt'
5
+ require 'net/http'
6
+ require 'json'
7
+
8
+ module OmniAuth
9
+ module Strategies
10
+ # OmniAuth strategy for DoorDash (Delivery provider).
11
+ #
12
+ # DoorDash does NOT use OAuth2. Instead, each API request is authenticated
13
+ # via a JWT signed with HMAC-SHA256 using developer credentials:
14
+ # - developer_id: identifies the developer account
15
+ # - key_id: identifies the specific credential set
16
+ # - signing_secret: used to sign the JWT (HS256)
17
+ #
18
+ # JWTs are valid for up to 30 minutes. A new JWT is generated per request.
19
+ #
20
+ # Since DoorDash has no OAuth user-redirect flow, this strategy implements
21
+ # a credential-based "connection" flow where the merchant enters their
22
+ # DoorDash store ID and we validate it by calling the DoorDash API.
23
+ #
24
+ # API docs: https://developer.doordash.com/en-US/docs/drive/how_to/JWTs/
25
+ #
26
+ # Usage:
27
+ # config.omniauth :doordash_oauth2,
28
+ # developer_id: ENV["DOORDASH_DEVELOPER_ID"],
29
+ # key_id: ENV["DOORDASH_KEY_ID"],
30
+ # signing_secret: ENV["DOORDASH_SIGNING_SECRET"]
31
+ #
32
+ class DoordashOauth2
33
+ include OmniAuth::Strategy
34
+
35
+ option :name, 'doordash_oauth2'
36
+
37
+ # DoorDash credentials (not standard OAuth2 client_id/secret)
38
+ option :developer_id, nil
39
+ option :key_id, nil
40
+ option :signing_secret, nil
41
+
42
+ option :api_base_url, 'https://openapi.doordash.com'
43
+
44
+ # DoorDash JWT header fields
45
+ JWT_VERSION = 1
46
+ JWT_ALGORITHM = 'HS256'
47
+ TOKEN_LIFETIME = 300 # 5 minutes (conservative; max is 30 min)
48
+
49
+ uid { raw_info['store_id'] || raw_info['id'] }
50
+
51
+ info do
52
+ {
53
+ name: raw_info['store_name'],
54
+ business_name: raw_info['store_name'],
55
+ email: nil # DoorDash doesn't provide merchant email
56
+ }
57
+ end
58
+
59
+ extra do
60
+ { raw_info: raw_info }
61
+ end
62
+
63
+ def raw_info
64
+ @raw_info ||= fetch_store_info
65
+ end
66
+
67
+ # Generate a signed JWT for DoorDash API authentication.
68
+ #
69
+ # @return [String] signed JWT
70
+ def generate_jwt
71
+ now = Time.now.to_i
72
+ payload = {
73
+ aud: 'doordash',
74
+ iss: options[:developer_id],
75
+ kid: options[:key_id],
76
+ exp: now + TOKEN_LIFETIME,
77
+ iat: now
78
+ }
79
+ header = {
80
+ algorithm: JWT_ALGORITHM,
81
+ 'dd-ver': "DD-JWT-V#{JWT_VERSION}"
82
+ }
83
+ decoded_secret = decode_signing_secret(options[:signing_secret])
84
+ JWT.encode(payload, decoded_secret, JWT_ALGORITHM, header)
85
+ end
86
+
87
+ # Decode the DoorDash signing secret from base64url.
88
+ # DoorDash provides the secret as a base64url-encoded string.
89
+ # Adds padding if needed before decoding.
90
+ #
91
+ # @param secret [String] base64url-encoded signing secret
92
+ # @return [String] decoded binary secret
93
+ def decode_signing_secret(secret)
94
+ padded = secret.to_s
95
+ padded += '=' * (4 - (padded.length % 4)) unless (padded.length % 4).zero?
96
+ Base64.urlsafe_decode64(padded)
97
+ end
98
+
99
+ private
100
+
101
+ def fetch_store_info
102
+ response = request_businesses
103
+ parsed = JSON.parse(response.body)
104
+ build_store_info(parsed)
105
+ rescue StandardError => e
106
+ log(:warn, "Failed to fetch store info: #{e.message}")
107
+ { 'store_id' => options[:developer_id] }
108
+ end
109
+
110
+ def request_businesses
111
+ jwt = generate_jwt
112
+ uri = URI("#{options[:api_base_url]}/developer/v1/businesses")
113
+ http = Net::HTTP.new(uri.host, uri.port)
114
+ http.use_ssl = true
115
+
116
+ req = Net::HTTP::Get.new(uri)
117
+ req['Authorization'] = "Bearer #{jwt}"
118
+ req['Content-Type'] = 'application/json'
119
+
120
+ http.request(req)
121
+ end
122
+
123
+ def build_store_info(parsed)
124
+ businesses = parsed['businesses'] || []
125
+ business = businesses.first
126
+
127
+ if business
128
+ store_id = business['external_business_id'] || business['id']
129
+ { 'store_id' => store_id, 'store_name' => business['name'], 'id' => store_id }
130
+ else
131
+ { 'store_id' => options[:developer_id] }
132
+ end
133
+ end
134
+
135
+ def log(level, message)
136
+ OmniAuth.logger.send(level, "[DoordashOauth2] #{message}")
137
+ end
138
+ end
139
+ end
140
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'omniauth'
4
+ require 'omniauth/doordash_oauth2/version'
5
+ require 'omniauth/strategies/doordash_oauth2'
metadata ADDED
@@ -0,0 +1,164 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: omniauth-doordash-oauth2
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - dan1d
8
+ bindir: bin
9
+ cert_chain: []
10
+ date: 1980-01-02 00:00:00.000000000 Z
11
+ dependencies:
12
+ - !ruby/object:Gem::Dependency
13
+ name: jwt
14
+ requirement: !ruby/object:Gem::Requirement
15
+ requirements:
16
+ - - "~>"
17
+ - !ruby/object:Gem::Version
18
+ version: '2.0'
19
+ type: :runtime
20
+ prerelease: false
21
+ version_requirements: !ruby/object:Gem::Requirement
22
+ requirements:
23
+ - - "~>"
24
+ - !ruby/object:Gem::Version
25
+ version: '2.0'
26
+ - !ruby/object:Gem::Dependency
27
+ name: omniauth
28
+ requirement: !ruby/object:Gem::Requirement
29
+ requirements:
30
+ - - "~>"
31
+ - !ruby/object:Gem::Version
32
+ version: '2.0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '2.0'
40
+ - !ruby/object:Gem::Dependency
41
+ name: rspec
42
+ requirement: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.12'
47
+ type: :development
48
+ prerelease: false
49
+ version_requirements: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '3.12'
54
+ - !ruby/object:Gem::Dependency
55
+ name: rubocop
56
+ requirement: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.75'
61
+ type: :development
62
+ prerelease: false
63
+ version_requirements: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '1.75'
68
+ - !ruby/object:Gem::Dependency
69
+ name: rubocop-rspec
70
+ requirement: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '3.5'
75
+ type: :development
76
+ prerelease: false
77
+ version_requirements: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '3.5'
82
+ - !ruby/object:Gem::Dependency
83
+ name: simplecov
84
+ requirement: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '0.22'
89
+ type: :development
90
+ prerelease: false
91
+ version_requirements: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '0.22'
96
+ - !ruby/object:Gem::Dependency
97
+ name: simplecov-json
98
+ requirement: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '0.2'
103
+ type: :development
104
+ prerelease: false
105
+ version_requirements: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '0.2'
110
+ - !ruby/object:Gem::Dependency
111
+ name: webmock
112
+ requirement: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '3.18'
117
+ type: :development
118
+ prerelease: false
119
+ version_requirements: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '3.18'
124
+ description: An OmniAuth strategy for authenticating with DoorDash using JWT-based
125
+ authentication (HMAC-SHA256). DoorDash does not use standard OAuth2 flows; instead,
126
+ each API request is signed with a JWT using developer credentials.
127
+ email:
128
+ - dan1d@users.noreply.github.com
129
+ executables: []
130
+ extensions: []
131
+ extra_rdoc_files: []
132
+ files:
133
+ - CHANGELOG.md
134
+ - LICENSE.txt
135
+ - README.md
136
+ - lib/omniauth-doordash-oauth2.rb
137
+ - lib/omniauth/doordash_oauth2/version.rb
138
+ - lib/omniauth/strategies/doordash_oauth2.rb
139
+ homepage: https://github.com/dan1d/omniauth-doordash-oauth2
140
+ licenses:
141
+ - MIT
142
+ metadata:
143
+ homepage_uri: https://github.com/dan1d/omniauth-doordash-oauth2
144
+ source_code_uri: https://github.com/dan1d/omniauth-doordash-oauth2
145
+ changelog_uri: https://github.com/dan1d/omniauth-doordash-oauth2/blob/main/CHANGELOG.md
146
+ rubygems_mfa_required: 'true'
147
+ rdoc_options: []
148
+ require_paths:
149
+ - lib
150
+ required_ruby_version: !ruby/object:Gem::Requirement
151
+ requirements:
152
+ - - ">="
153
+ - !ruby/object:Gem::Version
154
+ version: 3.1.0
155
+ required_rubygems_version: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ requirements: []
161
+ rubygems_version: 3.6.9
162
+ specification_version: 4
163
+ summary: OmniAuth strategy for DoorDash using JWT authentication
164
+ test_files: []