Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[DOCS]: Implementation of GitHub App user authentication token with expiring disabled #517

Open
1 task done
saddam-azad opened this issue Aug 11, 2023 · 6 comments
Open
1 task done
Labels
Type: Documentation Improvements or additions to documentation
Projects

Comments

@saddam-azad
Copy link

Describe the need

Attempting to create a GitHub App user authentication token with expiring disabled. (Sidenote, is it the same as a user-to-server token? The language on docs is not clear.)

  1. We have a Github App

  2. We have Opted-Out of the "User-to-server token expiration" in App Settings > Optional features.

  3. Attempting to generate a non-expiring token using @octokit/auth-app

  4. We tried two approaches to generate a token, using JWT and Installation token as Authentication Objects for the createOAuthUserAuth function.

import * as dotenv from 'dotenv';
import { Octokit } from '@octokit/core';
import { createAppAuth, createOAuthUserAuth } from '@octokit/auth-app';

/**
 * Using Github App (JWT)
 */
export const getTokenUsingJwt = async (service: any, SecretKey: any): Promise<string> => {
    try {
        dotenv.config();
        const appId = process.env.APP_ID as string;
        const privateKey = SecretKey?.SecretString as string;
        const clientId = process.env.CLIENT_ID as string;
        const clientSecret = process.env.CLIENT_SECRET as string;
        const installationId = service.metadata.installation as number;

        // Create a new Octokit instance using App Authentication
        const appAuth = createAppAuth({
            appId: Number(appId),
            privateKey,
        });

        // Authenticate as the GitHub App
        const appAuthentication = await appAuth({
            type: 'app',
        });
        const appAuthenticationToken = appAuthentication.token;
        // console.log('App authentication token', appAuthenticationToken);

        // Create an OAuth user authentication for the specified installation
        const authenticated = createOAuthUserAuth({
            clientType: "github-app",
            clientId,
            clientSecret,
            token: appAuthenticationToken,
        });

        // Get the user-to-server authentication token
        const { token, type, tokenType } = await authenticated();

        return token; // type: token, tokenType: oauth
        
    } catch(error) {
        throw error;
    }
}

/**
 * Using Installation
 */
export const getTokenUsingInstallation = async (service: any, SecretKey: any): Promise<string> => {
    try {
        dotenv.config();
        const appId = process.env.APP_ID as string;
        const privateKey = SecretKey?.SecretString as string;
        const clientId = process.env.CLIENT_ID as string;
        const clientSecret = process.env.CLIENT_SECRET as string;
        const installationId = service.metadata.installation as number;

        // Step 1: Authenticate as your GitHub App using a JWT
        const appAuth = createAppAuth({
            appId: Number(appId),
            privateKey: privateKey,
        });

        // Create an installation access token to act on behalf of the GitHub App installation
        const installationAuthentication = await appAuth({ 
            type: 'installation', 
            installationId 
        });
        const installationAccessToken = installationAuthentication.token;
        // console.log('Installation token', installationAccessToken);

        // Step 2: Create OAuth user-to-server access token using the installation access token
        const authenticated = createOAuthUserAuth({
            clientType: "github-app",
            clientId,
            clientSecret,
            token: installationAccessToken,
        });

        // Get the user-to-server access token
        const { token, type, tokenType } = await authenticated({ type: 'get' });

        return token; // type: token, tokenType: oauth
        
    } catch(error) {
        throw error;
    }
}

JWT approach produces a token such as:
eyJhkGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJpYXQiOjE2OTE2NjkzMDMsImV4cCI6MTY5MTY2OTkwMywiaXNzIjoyNzI1NjZ9.gaUOyjWkbm7NrfhNayPYwK1EzMyeUlEuWTTd7OEpBS91pY6wjCJx_giNOJBzNf0gzMwCyycxtjUCiMU1g6J0xgaYH_2iZxNeoakKYztHxccG8lzBUDCaTwgMBeeFxErlsW02WX8-b8nh0kEr07prYr7mwZFs2i5vEjgJznvk7NU7rzknXBzPeac5DZNq-NO6ikb_BTlMq1z7sW9SXU7xrEM8uHyVvk2KIYvkpwqRvoFBAeWuIIP1UotORxnLqAcLa5AIgOB3vg3Fonhv7d65NfbT9S1A6bfNjXJ25fZvzLoSRgCjTmR1St4MqgsK6O71ThjEk_GELnAm2LEwBt_VuQ

The installation token approach produces a token such as this. However, this expires in 1 hour.
ghs_knjVAQQVgpez6y4x6iBtm1BPkCLTlv33Zryn

Both tokens return tokenType as oauth

The documentation for @octokit/auth-app is not clear on how to disable expiry.

SDK Version

3.388.0

API Version

No response

Relevant log output

No response

Code of Conduct

  • I agree to follow this project's Code of Conduct
@saddam-azad saddam-azad added Status: Triage This is being looked at and prioritized Type: Documentation Improvements or additions to documentation labels Aug 11, 2023
@ghost ghost added this to Documentation in JS Aug 11, 2023
@github-actions
Copy link
Contributor

👋 Hi! Thank you for this contribution! Just to let you know, our GitHub SDK team does a round of issue and PR reviews twice a week, every Monday and Friday! We have a process in place for prioritizing and responding to your input. Because you are a part of this community please feel free to comment, add to, or pick up any issues/PRs that are labled with Status: Up for grabs. You & others like you are the reason all of this works! So thank you & happy coding! 🚀

@gr2m
Copy link
Contributor

gr2m commented Aug 11, 2023

Your code does not create a user-to-server token. What exactly is your use case?

You user-to-server token can only be created using the web flow or the device flow. The Device flow needs to be enabled in the app in order for it to work.

The installation token approach produces a token such as this. However, this expires in 1 hour.
ghs_knjVAQQVgpez6y4x6iBtm1BPkCLTlv33Zryn

Installation access tokens always expire after one hour. But if you pass the strategy to an Octokit constructor it will renew the token for you.

@saddam-azad
Copy link
Author

  1. User has Github App installed on account/org during account on-boarding.
  2. User create a service CI/CD pipeline without a Personal Access Token (not possible in our use case)
  3. The Application creates a token that does not expire in order for the pipeline to keep watching the repos for changes.
  4. The App also periodically regenerates the tokens on behalf of the User, as best practice suggests.

Would you be kind enough to update the documentation or respond with sample code on how to generate a user-to-server token without expiry?

@gr2m
Copy link
Contributor

gr2m commented Aug 11, 2023

how to generate a user-to-server token

you cannot generate a user-to-server token, it would mean to authenticate as a user through a GitHub App. User involvement is required for that flow, usually an OAuth web flow

It sounds like a common use case, I'd usually use an action like this to create an installation access token based on app credentials: https://github.com/gr2m/github-app-token-action and then use the resulting token in successive steps. However that token expires after one hour.

For cases where a token is needed for tasks that take more than an hour, the tasks themselves need to accept the GitHub App credentials and create installation access tokens as needed

@kfcampbell kfcampbell removed the Status: Triage This is being looked at and prioritized label Aug 11, 2023
@saddam-azad
Copy link
Author

User involvement is required for that flow, usually an OAuth web flow

  • User selects a repo (using installation token), clicks Continue
  • Popup/Redirect to Github OAuth web flow (confirms permissions over selected repo)
  • Return to page (with the code parameter)

At this point, we can create a user-to-server token without expiry for further use?

@gr2m
Copy link
Contributor

gr2m commented Aug 16, 2023

yeah that should work. Note that user-to-server token expiration is a setting on the github app ("Optional features" tab in app settings)

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Type: Documentation Improvements or additions to documentation
Projects
Status: 🛑 Blocked/Awaiting Response
JS
  
Documentation
Development

No branches or pull requests

3 participants