Authenticate ensures the session is authorized. It returns (nil, nil) once a token is available, so the caller may proceed. It returns (&Outcome{UserAction}, nil) when the user must complete the flow out of band; the flow continues in the background and the caller should show the action and have th
(ctx context.Context, prompter Prompter)
| 137 | // Only one flow runs at a time. Concurrent callers either join a running secure |
| 138 | // flow, receive the pending user action, or are told to retry shortly. |
| 139 | func (m *Manager) Authenticate(ctx context.Context, prompter Prompter) (*Outcome, error) { |
| 140 | if m.AccessToken() != "" { |
| 141 | return nil, nil |
| 142 | } |
| 143 | |
| 144 | m.mu.Lock() |
| 145 | switch m.status { |
| 146 | case statusAwaitingUser: |
| 147 | ua := m.pending |
| 148 | m.mu.Unlock() |
| 149 | return &Outcome{UserAction: ua}, nil |
| 150 | case statusStarting: |
| 151 | m.mu.Unlock() |
| 152 | return &Outcome{UserAction: &UserAction{ |
| 153 | Message: "GitHub authorization is already in progress. Please retry your request in a few seconds.", |
| 154 | }}, nil |
| 155 | case statusInProgress: |
| 156 | done := m.done |
| 157 | m.mu.Unlock() |
| 158 | return m.joinWait(ctx, done) |
| 159 | } |
| 160 | |
| 161 | // Idle: this call owns the new flow. |
| 162 | m.status = statusStarting |
| 163 | m.lastErr = nil |
| 164 | m.done = make(chan struct{}) |
| 165 | done := m.done |
| 166 | m.mu.Unlock() |
| 167 | |
| 168 | plan, err := m.begin(prompter) |
| 169 | if err != nil { |
| 170 | m.complete(nil, err) |
| 171 | return nil, err |
| 172 | } |
| 173 | |
| 174 | m.mu.Lock() |
| 175 | if plan.userAction != nil { |
| 176 | m.status = statusAwaitingUser |
| 177 | m.pending = plan.userAction |
| 178 | } else { |
| 179 | m.status = statusInProgress |
| 180 | } |
| 181 | m.mu.Unlock() |
| 182 | |
| 183 | bgCtx, cancel := context.WithTimeout(context.Background(), DefaultAuthTimeout) |
| 184 | go m.runFlow(bgCtx, cancel, plan) |
| 185 | |
| 186 | if plan.userAction != nil { |
| 187 | return &Outcome{UserAction: plan.userAction}, nil |
| 188 | } |
| 189 | return m.joinWait(ctx, done) |
| 190 | } |
| 191 | |
| 192 | // runFlow executes a prepared flow in the background and records the result. The |
| 193 | // optional display prompt runs concurrently: a decline (or other failure) aborts |
nothing calls this directly
no test coverage detected