beginDevice prepares the device authorization flow. It requests a device code up front (so the code can be displayed) and selects a display channel: URL elicitation, then form elicitation, then a tool-response message.
(prompter Prompter)
| 157 | // up front (so the code can be displayed) and selects a display channel: |
| 158 | // URL elicitation, then form elicitation, then a tool-response message. |
| 159 | func (m *Manager) beginDevice(prompter Prompter) (*flowPlan, error) { |
| 160 | oc := m.oauth2Config("") |
| 161 | |
| 162 | ctx, cancel := context.WithTimeout(context.Background(), deviceAuthTimeout) |
| 163 | defer cancel() |
| 164 | da, err := oc.DeviceAuth(ctx) |
| 165 | if err != nil { |
| 166 | return nil, fmt.Errorf("requesting device code: %w", err) |
| 167 | } |
| 168 | |
| 169 | run := func(ctx context.Context) (*oauth2.Token, error) { |
| 170 | tok, err := oc.DeviceAccessToken(ctx, da) |
| 171 | if err != nil { |
| 172 | return nil, fmt.Errorf("awaiting device authorization: %w", err) |
| 173 | } |
| 174 | return tok, nil |
| 175 | } |
| 176 | |
| 177 | // As with PKCE, the manual instructions double as the runtime fallback, so |
| 178 | // build them once and reuse for both display plans and the last resort. |
| 179 | manual := &UserAction{ |
| 180 | URL: da.VerificationURI, |
| 181 | UserCode: da.UserCode, |
| 182 | Message: fmt.Sprintf( |
| 183 | "%s\n\nAfter authorizing, retry your request.\n\n%s", |
| 184 | deviceInstruction(da), securityAdvisory, |
| 185 | ), |
| 186 | } |
| 187 | |
| 188 | if canPromptURL(prompter) { |
| 189 | display := func(ctx context.Context) error { |
| 190 | return prompter.PromptURL(ctx, Prompt{ |
| 191 | Message: fmt.Sprintf("Enter code %s to authorize the GitHub MCP Server.", da.UserCode), |
| 192 | URL: da.VerificationURI, |
| 193 | UserCode: da.UserCode, |
| 194 | }) |
| 195 | } |
| 196 | return &flowPlan{run: run, display: display, fallback: manual}, nil |
| 197 | } |
| 198 | |
| 199 | if canPromptForm(prompter) { |
| 200 | display := func(ctx context.Context) error { |
| 201 | return prompter.PromptForm(ctx, Prompt{ |
| 202 | Message: deviceInstruction(da), |
| 203 | URL: da.VerificationURI, |
| 204 | UserCode: da.UserCode, |
| 205 | }) |
| 206 | } |
| 207 | return &flowPlan{run: run, display: display, fallback: manual}, nil |
| 208 | } |
| 209 | |
| 210 | return &flowPlan{run: run, userAction: manual}, nil |
| 211 | } |
| 212 | |
| 213 | // securityAdvisory nudges users on clients without URL elicitation to ask their |
| 214 | // vendor for it, since it keeps the authorization URL out of the model context. |
no test coverage detected