jsonwebtoken#decode TypeScript Examples

The following examples show how to use jsonwebtoken#decode. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: server.ts    From one-platform with MIT License 6 votes vote down vote up
getServer = () => {
  /* Create GraphQL Server  */
  const server = new ApolloServer({
    schema,
    context: ({ req }) => {
      const userId = req.headers['x-op-user-id'];

      let roles = req.headers['x-op-user-roles']
      if (typeof roles === 'string') {
        roles = roles.split(',').map(role => role.trim());
      }

      const tokenHeader = req.headers['x-op-token'];
      const token = Array.isArray(tokenHeader) ? tokenHeader.join('') : tokenHeader;
      const decodedToken = token ? decode(token) : null;

      let user = {};
      if (!!decodedToken && typeof decodedToken !== 'string') {
        user = {
          id: decodedToken[TOKEN_USER_ID_FIELD],
          name: decodedToken[TOKEN_NAME_FIELD],
          username: decodedToken[TOKEN_USERNAME_FIELD],
          email: decodedToken[TOKEN_EMAIL_FIELD],
        };
      }

      return {
        userId,
        roles,
        token,
        user,
      };
    },
    plugins: [ApolloServerPluginInlineTrace()],
    debug: NODE_ENV !== 'production',
  });

  return server;
}
Example #2
Source File: EnsureAuthenticatedMiddleware.ts    From umbriel with MIT License 6 votes vote down vote up
async handle(
    request: EnsureAuthenticatedMiddlewareRequest
  ): Promise<HttpResponse> {
    try {
      const { accessToken } = request

      if (accessToken) {
        try {
          const decoded = decode(accessToken) as DecodedJwt

          return ok({ userId: decoded.sub })
        } catch (err) {
          return forbidden(new AccessDeniedError())
        }
      }

      return forbidden(new AccessDeniedError())
    } catch (error) {
      return fail(error)
    }
  }
Example #3
Source File: client_credentials.grant.spec.ts    From ts-oauth2-server with MIT License 6 votes vote down vote up
export function expectTokenResponse(tokenResponse: ResponseInterface) {
  const decodedToken: any = decode(tokenResponse.body.access_token);

  expect(tokenResponse.status).toBe(200);
  expect(tokenResponse.headers["cache-control"]).toBe("no-store");
  expect(tokenResponse.headers["pragma"]).toBe("no-cache");
  expect(tokenResponse.body.token_type).toBe("Bearer");
  expect(tokenResponse.body.expires_in).toBe(3600);
  expect(tokenResponse.body.access_token).toMatch(REGEX_ACCESS_TOKEN);

  expect(decodedToken.exp).toBeTruthy();
  expect(decodedToken.jti).toBeTruthy();

  return decodedToken;
}
Example #4
Source File: permissions.ts    From gobarber-project with MIT License 6 votes vote down vote up
async function decoder(request: Request): Promise<User | undefined> {
  const authHeader = request.headers.authorization || "";
  const userRepository = getCustomRepository(UserRepository);

  const [, token] = authHeader?.split(" ");

  const payload = decode(token);

  const user = await userRepository.findOne(payload?.sub, {
    relations: ["roles"],
  });

  return user;
}
Example #5
Source File: MiddlewareAdapter.ts    From keycloak-lambda-authorizer with Apache License 2.0 6 votes vote down vote up
getTokenString(req:any) {
    const tokenString = req.headers.authorization;
    if (!tokenString) {
      throw new Error('Expected \'headers.authorization\' parameter to be set');
    }
    const match = tokenString.match(/^Bearer (.*)$/i);
    if (!match || match.length < 2) {
      throw new Error(`Invalid Authorization token - '${tokenString}' does not match 'Bearer .*'`);
    }
    req.jwt = {token: match[1], payload: decode(match[1])};
    return match[1];
  }
Example #6
Source File: JWT.ts    From ZenTS with MIT License 5 votes vote down vote up
public static decode<T>(token: string): T {
    return decode(token, {
      json: true,
    }) as T
  }
Example #7
Source File: authorization_server.spec.ts    From ts-oauth2-server with MIT License 4 votes vote down vote up
// base64urlencode(crypto.createHash("sha256").update(codeVerifier).digest());

describe("authorization_server", () => {
  let authorizationServer: AuthorizationServer;
  let refreshGrant: RefreshTokenGrant;

  let user: OAuthUser;
  let client: OAuthClient;
  let accessToken: OAuthToken;
  let scope1: OAuthScope;
  let scope2: OAuthScope;

  beforeEach(() => {
    authorizationServer = new AuthorizationServer(
      inMemoryAuthCodeRepository,
      inMemoryClientRepository,
      inMemoryAccessTokenRepository,
      inMemoryScopeRepository,
      inMemoryUserRepository,
      new JwtService("secret-key"),
    );
    refreshGrant = authorizationServer.getGrant("refresh_token");
    authorizationServer.enableGrantType("authorization_code");
    authorizationServer.enableGrantType("client_credentials");
    authorizationServer.enableGrantType("implicit");
    authorizationServer.enableGrantType("password");
    authorizationServer.enableGrantType("refresh_token");

    user = { id: "abc123" };
    scope1 = { name: "scope-1" };
    scope2 = { name: "scope-2" };
    client = {
      id: "1",
      name: "test client",
      secret: "super-secret-secret",
      redirectUris: ["http://localhost"],
      allowedGrants: ["client_credentials"],
      scopes: [],
    };
    inMemoryDatabase.scopes[scope1.name] = scope1;
    inMemoryDatabase.scopes[scope2.name] = scope2;
    inMemoryDatabase.users[user.id] = user;
  });

  it("can enable client_credentials grant", async () => {
    // arrange
    inMemoryDatabase.clients[client.id] = client;

    const basicAuth = "Basic " + base64encode(`${client.id}:${client.secret}`);
    const request = new OAuthRequest({
      headers: {
        authorization: basicAuth,
      },
      body: {
        grant_type: "client_credentials",
      },
    });

    // act
    const tokenResponse = await authorizationServer.respondToAccessTokenRequest(request);

    // assert
    expectTokenResponse(tokenResponse);
  });

  it("validateAuthorizationRequest", async () => {
    client = {
      id: "authcodeclient",
      name: "test auth code client",
      secret: undefined,
      redirectUris: ["http://localhost"],
      allowedGrants: ["authorization_code"],
      scopes: [scope1, scope2],
    };
    inMemoryDatabase.clients[client.id] = client;

    const request = new OAuthRequest({
      query: {
        response_type: "code",
        client_id: client.id,
        redirect_uri: "http://localhost",
        scope: "scope-1 scope-2",
        state: "state-is-a-secret",
        code_challenge: codeChallenge,
        code_challenge_method: "S256",
      },
    });
    const authorizationRequest = await authorizationServer.validateAuthorizationRequest(request);

    expect(authorizationRequest.isAuthorizationApproved).toBe(false);
    expect(authorizationRequest.client.id).toBe(client.id);
    expect(authorizationRequest.client.name).toBe(client.name);
    expect(authorizationRequest.redirectUri).toBe("http://localhost");
    expect(authorizationRequest.state).toBe("state-is-a-secret");
    expect(authorizationRequest.codeChallenge).toBe(codeChallenge);
    expect(authorizationRequest.codeChallengeMethod).toBe("S256");
    expect(authorizationRequest.scopes).toStrictEqual([scope1, scope2]);
  });

  it("is successful", async () => {
    client = {
      id: "authcodeclient",
      name: "test auth code client",
      secret: undefined,
      redirectUris: ["http://localhost"],
      allowedGrants: ["authorization_code"],
      scopes: [scope1, scope2],
    };
    inMemoryDatabase.clients[client.id] = client;

    const authorizationRequest = new AuthorizationRequest("authorization_code", client, "http://localhost");
    authorizationRequest.isAuthorizationApproved = true;
    authorizationRequest.codeChallengeMethod = "S256";
    authorizationRequest.codeChallenge = codeChallenge;
    authorizationRequest.user = user;

    const response = await authorizationServer.completeAuthorizationRequest(authorizationRequest);
    const authorizeResponseQuery = querystring.parse(response.headers.location.split("?")[1]);
    const decodedCode: IAuthCodePayload = <IAuthCodePayload>decode(String(authorizeResponseQuery.code));

    expect(decodedCode.client_id).toBe(client.id);
    expect(decodedCode.redirect_uri).toBe("http://localhost");
  });

  describe("option requirePKCE", () => {
    beforeEach(() => {
      client = {
        id: "authcodeclient",
        name: "test auth code client",
        secret: undefined,
        redirectUris: ["http://localhost"],
        allowedGrants: ["authorization_code"],
        scopes: [scope1, scope2],
      };
      inMemoryDatabase.clients[client.id] = client;
    });

    test("auth server that does not requirePKCE succeeds for request without code_challenge", async () => {
      authorizationServer.setOptions({ requiresPKCE: false });
      authorizationServer.enableGrantType("authorization_code");
      const request = new OAuthRequest({
        query: {
          response_type: "code",
          client_id: client.id,
          scope: scope1.name,
          state: "state-is-a-secret",
        },
      });

      // act
      const validResponse = await authorizationServer.validateAuthorizationRequest(request);
      validResponse.user = user;
      validResponse.isAuthorizationApproved = true;
      const response = await authorizationServer.completeAuthorizationRequest(validResponse);

      // assert
      const authorizeResponseQuery = querystring.parse(response.headers.location.split("?")[1]);
      const decodedCode: IAuthCodePayload = <IAuthCodePayload>decode(String(authorizeResponseQuery.code));
      expect(decodedCode.client_id).toBe(client.id);
      expect(decodedCode.redirect_uri).toBe("http://localhost");
      expect(decodedCode.code_challenge).toBeUndefined();
    });

    test("auth server requiring pkce throws if request is missing code_challenge", async () => {
      authorizationServer = new AuthorizationServer(
        inMemoryAuthCodeRepository,
        inMemoryClientRepository,
        inMemoryAccessTokenRepository,
        inMemoryScopeRepository,
        inMemoryUserRepository,
        new JwtService("secret-key"),
      );
      authorizationServer.enableGrantType("authorization_code");
      const request = new OAuthRequest({
        query: {
          response_type: "code",
          client_id: client.id,
          scope: scope1.name,
          state: "state-is-a-secret",
        },
      });

      // act
      const response = authorizationServer.validateAuthorizationRequest(request);

      // assert
      await expect(response).rejects.toThrowError(
        /The authorization server requires public clients to use PKCE RFC-7636/,
      );
    });
  });

  it("respondToAccessTokenRequest is successful", async () => {
    // arrange
    client = {
      id: "a854eb18-c3df-41a3-ab6b-5d96f787f105",
      name: "test client",
      secret: "super-secret-secret",
      redirectUris: ["http://localhost"],
      allowedGrants: ["refresh_token"],
      scopes: [scope1, scope2],
    };
    accessToken = {
      accessToken: "176fa0a5-acc7-4ef7-8ff3-17cace20f83e",
      accessTokenExpiresAt: DateInterval.getDateEnd("1h"),
      refreshToken: "8a0d01db-4da7-4250-8f18-f6c096b1912e",
      refreshTokenExpiresAt: DateInterval.getDateEnd("1h"),
      client,
      scopes: [scope1, scope2],
    };
    inMemoryDatabase.clients[client.id] = client;
    inMemoryDatabase.tokens[accessToken.accessToken] = accessToken;
    const bearerResponse = await refreshGrant.makeBearerTokenResponse(client, accessToken);
    const request = new OAuthRequest({
      body: {
        grant_type: "refresh_token",
        client_id: client.id,
        client_secret: client.secret,
        refresh_token: bearerResponse.body.refresh_token,
        scope: "scope-1",
      },
    });

    // act
    const tokenResponse = await authorizationServer.respondToAccessTokenRequest(request);

    // assert
    expectTokenResponse(tokenResponse);
    expect(tokenResponse.body.scope).toBe("scope-1");
  });
});
Example #8
Source File: auth_code.grant.spec.ts    From ts-oauth2-server with MIT License 4 votes vote down vote up
describe("authorization_code grant", () => {
  let user: OAuthUser;
  let client: OAuthClient;
  let scope1: OAuthScope;
  let grant: AuthCodeGrant;

  let request: OAuthRequest;

  const codeVerifier = "qqVDyvlSezXc64NY5Rx3BbL_aT7c2xEBgoJP9domepFZLEjo9ln8EA"; // base64urlencode(crypto.randomBytes(40));
  const codeChallenge = "hA3IxucyJC0BsZH9zdYvGeK0ck2dC-seLBn20l18Iws"; // base64urlencode(crypto.createHash("sha256").update(codeVerifier).digest());

  beforeEach(() => {
    request = new OAuthRequest();

    user = { id: "abc123", email: "[email protected]" };
    scope1 = { name: "scope-1" };

    client = {
      id: "authcodeclient",
      name: "test auth code client",
      secret: undefined,
      redirectUris: ["http://example.com"],
      allowedGrants: ["authorization_code"],
      scopes: [],
    };

    grant = new AuthCodeGrant(
      inMemoryAuthCodeRepository,
      inMemoryClientRepository,
      inMemoryAccessTokenRepository,
      inMemoryScopeRepository,
      inMemoryUserRepository,
      new JwtService("secret-key"),
    );

    inMemoryDatabase.clients[client.id] = client;
    inMemoryDatabase.users[user.id] = user;
    inMemoryDatabase.scopes[scope1.name] = scope1;
  });

  describe("can respond to authorization request", () => {
    let validQueryData: any;

    beforeEach(() => {
      validQueryData = {
        response_type: "code",
        client_id: client.id,
        redirect_uri: "http://example.com",
        state: "state-is-a-secret",
        code_challenge: codeChallenge,
        code_challenge_method: "S256",
      };
    });

    it("returns true for valid request", async () => {
      request = new OAuthRequest({ query: validQueryData });

      expect(grant.canRespondToAuthorizationRequest(request)).toBe(true);
    });

    it("returns false for missing client_id", async () => {
      request = new OAuthRequest({
        query: {
          ...validQueryData,
          client_id: undefined,
        },
      });

      expect(grant.canRespondToAuthorizationRequest(request)).toBe(false);
    });

    it("returns false when response_type !== code", async () => {
      request = new OAuthRequest({
        query: {
          ...validQueryData,
          response_type: undefined,
        },
      });

      expect(grant.canRespondToAuthorizationRequest(request)).toBe(false);
    });
  });

  describe("validate authorization request", () => {
    it("is successful with S256 pkce", async () => {
      request = new OAuthRequest({
        query: {
          response_type: "code",
          client_id: client.id,
          // single object arrays is valid
          redirect_uri: ["http://example.com"],
          state: "state-is-a-secret",
          code_challenge: codeChallenge,
          code_challenge_method: "S256",
        },
      });
      const authorizationRequest = await grant.validateAuthorizationRequest(request);

      expect(authorizationRequest.isAuthorizationApproved).toBe(false);
      expect(authorizationRequest.client.id).toBe(client.id);
      expect(authorizationRequest.client.name).toBe(client.name);
      expect(authorizationRequest.redirectUri).toBe("http://example.com");
      expect(authorizationRequest.state).toBe("state-is-a-secret");
      expect(authorizationRequest.codeChallenge).toBe(codeChallenge);
      expect(authorizationRequest.codeChallengeMethod).toBe("S256");
      expect(authorizationRequest.scopes).toStrictEqual([]);
    });

    it("is successful with plain pkce", async () => {
      client.redirectUris = ["http://example.com"];
      inMemoryDatabase.clients[client.id] = client;
      const plainCodeChallenge = "qqVDyvlSezXc64NY5Rx3BbLaT7c2xEBgoJP9domepFZLEjo9ln8EAaSdfewSNY5Rx3BbL";
      request = new OAuthRequest({
        query: {
          response_type: "code",
          client_id: client.id,
          redirect_uri: "http://example.com",
          scope: "scope-1",
          state: "state-is-a-secret",
          code_challenge: base64urlencode(plainCodeChallenge), // code verifier plain
          code_challenge_method: "plain",
        },
      });
      const authorizationRequest = await grant.validateAuthorizationRequest(request);

      expect(authorizationRequest.isAuthorizationApproved).toBe(false);
      expect(authorizationRequest.client.id).toBe(client.id);
      expect(authorizationRequest.client.name).toBe(client.name);
      expect(authorizationRequest.redirectUri).toBe("http://example.com");
      expect(authorizationRequest.state).toBe("state-is-a-secret");
      expect(authorizationRequest.codeChallenge).toBe(base64urlencode(plainCodeChallenge));
      expect(authorizationRequest.codeChallengeMethod).toBe("plain");
      expect(authorizationRequest.scopes).toStrictEqual([{ name: "scope-1" }]);
    });

    it("is successful with request redirect uri with querystring", async () => {
      client.redirectUris = ["http://example.com"];
      inMemoryDatabase.clients[client.id] = client;
      const plainCodeChallenge = "qqVDyvlSezXc64NY5Rx3BbLaT7c2xEBgoJP9domepFZLEjo9ln8EAaSdfewSNY5Rx3BbL";
      request = new OAuthRequest({
        query: {
          response_type: "code",
          client_id: client.id,
          redirect_uri: "http://example.com?this_should_work=true&also-this=yeah",
          scope: "scope-1",
          state: "state-is-a-secret",
          code_challenge: base64urlencode(plainCodeChallenge), // code verifier plain
          code_challenge_method: "plain",
        },
      });
      const authorizationRequest = await grant.validateAuthorizationRequest(request);

      expect(authorizationRequest.isAuthorizationApproved).toBe(false);
      expect(authorizationRequest.client.id).toBe(client.id);
      expect(authorizationRequest.client.name).toBe(client.name);
      expect(authorizationRequest.redirectUri).toBe("http://example.com?this_should_work=true&also-this=yeah");
      expect(authorizationRequest.state).toBe("state-is-a-secret");
      expect(authorizationRequest.codeChallenge).toBe(base64urlencode(plainCodeChallenge));
      expect(authorizationRequest.codeChallengeMethod).toBe("plain");
      expect(authorizationRequest.scopes).toStrictEqual([{ name: "scope-1" }]);
    });

    it("is successful without using PKCE flow", async () => {
      // arrange
      request = new OAuthRequest({
        query: {
          response_type: "code",
          client_id: client.id,
          scope: "scope-1",
          state: "state-is-a-secret",
        },
      });
      grant.options.requiresPKCE = false;

      // act
      const authorizationRequest = await grant.validateAuthorizationRequest(request);

      // assert
      expect(authorizationRequest.isAuthorizationApproved).toBe(false);
      expect(authorizationRequest.client.id).toBe(client.id);
      expect(authorizationRequest.client.name).toBe(client.name);
      expect(authorizationRequest.redirectUri).toBe("http://example.com");
      expect(authorizationRequest.state).toBe("state-is-a-secret");
      expect(authorizationRequest.scopes).toStrictEqual([{ name: "scope-1" }]);
    });

    it("is successful with undefined redirect_uri", async () => {
      const plainCodeChallenge = "qqVDyvlSezXc64NY5Rx3BbLaT7c2xEBgoJP9domepFZLEjo9ln8EAaSdfewSNY5Rx3BbL";
      request = new OAuthRequest({
        query: {
          redirect_uri: undefined,
          response_type: "code",
          client_id: client.id,
          code_challenge: base64urlencode(plainCodeChallenge), // code verifier plain
        },
      });
      const authorizationRequest = await grant.validateAuthorizationRequest(request);

      expect(authorizationRequest.redirectUri).toBe("http://example.com");
    });

    it("throws when missing code_challenge pkce", async () => {
      request = new OAuthRequest({
        query: {
          response_type: "code",
          client_id: client.id,
          redirect_uri: "http://example.com",
          state: "state-is-a-secret",
          code_challenge_method: "plain",
        },
      });
      const authorizationRequest = grant.validateAuthorizationRequest(request);

      await expect(authorizationRequest).rejects.toThrowError(
        /The authorization server requires public clients to use PKCE RFC-7636/,
      );
    });

    it.skip("throws for invalid code_challenge pkce format regex", async () => {
      request = new OAuthRequest({
        query: {
          response_type: "code",
          client_id: client.id,
          redirect_uri: "http://example.com",
          state: "state-is-a-secret",
          code_challenge: "invalid-format(with!Invalid~characters",
          code_challenge_method: "S256",
        },
      });
      const authorizationRequest = grant.validateAuthorizationRequest(request);

      await expect(authorizationRequest).rejects.toThrowError(
        /Code challenge must follow the specifications of RFC-7636 and match/,
      );
    });

    it("throws for relative redirect_uri", async () => {
      request = new OAuthRequest({
        query: {
          response_type: "code",
          client_id: client.id,
          redirect_uri: "/foobydoo",
        },
      });
      const authorizationRequest = grant.validateAuthorizationRequest(request);

      await expect(authorizationRequest).rejects.toThrowError(/Check the `redirect_uri` parameter/);
    });

    it("throws for multiple redirect_uri args (array of strings)", async () => {
      request = new OAuthRequest({
        query: {
          response_type: "code",
          client_id: client.id,
          redirect_uri: ["http://example.com", "http://example2.com"],
        },
      });
      const authorizationRequest = grant.validateAuthorizationRequest(request);

      await expect(authorizationRequest).rejects.toThrowError(/Check the `redirect_uri` parameter/);
    });

    it("throws for redirect_uri containing url fragment", async () => {
      request = new OAuthRequest({
        query: {
          response_type: "code",
          client_id: client.id,
          redirect_uri: "http://example.com#fragle",
        },
      });
      const authorizationRequest = grant.validateAuthorizationRequest(request);

      await expect(authorizationRequest).rejects.toThrowError(
        /Redirection endpoint must not contain url fragment based on RFC6749/,
      );
    });
  });

  describe("complete authorization request", () => {
    it("is successful", async () => {
      const authorizationRequest = new AuthorizationRequest("authorization_code", client, "http://example.com");
      authorizationRequest.isAuthorizationApproved = true;
      authorizationRequest.codeChallengeMethod = "S256";
      authorizationRequest.codeChallenge = codeChallenge;
      authorizationRequest.state = "abc123";
      authorizationRequest.user = user;
      const response = await grant.completeAuthorizationRequest(authorizationRequest);
      const authorizeResponseQuery = querystring.parse(response.headers.location.split("?")[1]);
      const decodedCode: IAuthCodePayload = <IAuthCodePayload>decode(String(authorizeResponseQuery.code));

      expect(response.headers.location.includes("http://example.com?code=")).toBeTruthy();
      expect(decodedCode.client_id).toBe(client.id);
      expect(decodedCode.redirect_uri).toBe("http://example.com");
    });

    it("is successful with client with query", async () => {
      client.redirectUris = ["http://example.com?this_should_work=true"];
      inMemoryDatabase.clients[client.id] = client;

      const authorizationRequest = new AuthorizationRequest(
        "authorization_code",
        client,
        "http://example.com?this_should_work=true",
      );
      authorizationRequest.isAuthorizationApproved = true;
      authorizationRequest.codeChallengeMethod = "S256";
      authorizationRequest.codeChallenge = codeChallenge;
      authorizationRequest.state = "abc123";
      authorizationRequest.user = user;
      const response = await grant.completeAuthorizationRequest(authorizationRequest);
      const authorizeResponseQuery = querystring.parse(response.headers.location.split("?")[1]);
      const decodedCode: IAuthCodePayload = <IAuthCodePayload>decode(String(authorizeResponseQuery.code));

      expect(response.headers.location).toMatch(/http\:\/\/example\.com\?this_should_work=true\&code\=/);
      expect(decodedCode.client_id).toBe(client.id);
      expect(decodedCode.redirect_uri).toBe("http://example.com?this_should_work=true");
    });

    // it("uses clients redirect url if request ", async () => {});

    it("is successful without pkce flow", async () => {
      grant.options.requiresPKCE = false;
      const authorizationRequest = new AuthorizationRequest("authorization_code", client, "http://example.com");
      authorizationRequest.isAuthorizationApproved = true;
      authorizationRequest.state = "abc123";
      authorizationRequest.user = user;
      const response = await grant.completeAuthorizationRequest(authorizationRequest);
      const authorizeResponseQuery = querystring.parse(response.headers.location.split("?")[1]);
      const decodedCode: IAuthCodePayload = <IAuthCodePayload>decode(String(authorizeResponseQuery.code));

      expect(response.headers.location.includes("http://example.com?code=")).toBeTruthy();
      expect(decodedCode.client_id).toBe(client.id);
      expect(decodedCode.redirect_uri).toBe("http://example.com");
    });
  });

  describe("respond to access token request with code", () => {
    let authorizationRequest: AuthorizationRequest;
    let authorizationCode: string;

    beforeEach(async () => {
      authorizationRequest = new AuthorizationRequest("authorization_code", client, "http://example.com");
      authorizationRequest.isAuthorizationApproved = true;
      authorizationRequest.codeChallengeMethod = "S256";
      authorizationRequest.codeChallenge = codeChallenge;
      authorizationRequest.user = user;
      const redirectResponse = await grant.completeAuthorizationRequest(authorizationRequest);
      const authorizeResponseQuery = querystring.parse(redirectResponse.headers.location.split("?")[1]);
      authorizationCode = String(authorizeResponseQuery.code);
    });

    it("is successful with pkce S256", async () => {
      // act
      request = new OAuthRequest({
        body: {
          grant_type: "authorization_code",
          code: authorizationCode,
          redirect_uri: authorizationRequest.redirectUri,
          client_id: client.id,
          code_verifier: codeVerifier,
        },
      });
      const accessTokenResponse = await grant.respondToAccessTokenRequest(request, new DateInterval("1h"));

      // assert
      expectTokenResponse(accessTokenResponse);
      expect(accessTokenResponse.body.refresh_token).toMatch(REGEX_ACCESS_TOKEN);
    });

    it("is successful with pkce plain", async () => {
      authorizationRequest = new AuthorizationRequest("authorization_code", client, "http://example.com");
      authorizationRequest.isAuthorizationApproved = true;
      authorizationRequest.codeChallengeMethod = "plain";
      authorizationRequest.codeChallenge = codeChallenge;
      authorizationRequest.user = user;
      const redirectResponse = await grant.completeAuthorizationRequest(authorizationRequest);
      const authorizeResponseQuery = querystring.parse(redirectResponse.headers.location.split("?")[1]);
      authorizationCode = String(authorizeResponseQuery.code);

      // act
      request = new OAuthRequest({
        body: {
          grant_type: "authorization_code",
          code: authorizationCode,
          redirect_uri: authorizationRequest.redirectUri,
          client_id: client.id,
          code_verifier: codeChallenge,
        },
      });
      const accessTokenResponse = await grant.respondToAccessTokenRequest(request, new DateInterval("1h"));

      // assert
      expectTokenResponse(accessTokenResponse);
      expect(accessTokenResponse.body.refresh_token).toMatch(REGEX_ACCESS_TOKEN);
    });

    it("is successful without pkce", async () => {
      grant.options.requiresPKCE = false;
      authorizationRequest = new AuthorizationRequest("authorization_code", client, "http://example.com");
      authorizationRequest.isAuthorizationApproved = true;
      authorizationRequest.user = user;
      const redirectResponse = await grant.completeAuthorizationRequest(authorizationRequest);
      const authorizeResponseQuery = querystring.parse(redirectResponse.headers.location.split("?")[1]);
      authorizationCode = String(authorizeResponseQuery.code);

      // act
      request = new OAuthRequest({
        body: {
          grant_type: "authorization_code",
          code: authorizationCode,
          redirect_uri: authorizationRequest.redirectUri,
          client_id: client.id,
        },
      });
      const accessTokenResponse = await grant.respondToAccessTokenRequest(request, new DateInterval("1h"));

      // assert
      expectTokenResponse(accessTokenResponse);
      const decodedToken: any = decode(accessTokenResponse.body.access_token);
      expect(decodedToken?.email).toBe("[email protected]");
      expect(accessTokenResponse.body.refresh_token).toMatch(REGEX_ACCESS_TOKEN);
    });

    it("throws for confidential client when no secret is included in request", async () => {
      client = {
        ...client,
        secret: "auth-code-with-seccret",
      };
      inMemoryDatabase.clients[client.id] = client;

      // act
      request = new OAuthRequest({
        body: {
          grant_type: "authorization_code",
          code: authorizationCode,
          redirect_uri: authorizationRequest.redirectUri,
          client_id: client.id,
          code_verifier: codeVerifier + "invalid",
        },
      });
      const accessTokenResponse = grant.respondToAccessTokenRequest(request, new DateInterval("1h"));

      // assert
      await expect(accessTokenResponse).rejects.toThrowError(/Client authentication failed/);
    });

    it("throws for invalid code_verifier format", async () => {
      // act
      request = new OAuthRequest({
        body: {
          grant_type: "authorization_code",
          code: authorizationCode,
          redirect_uri: authorizationRequest.redirectUri,
          client_id: client.id,
          code_verifier: "invalid",
        },
      });
      const accessTokenResponse = grant.respondToAccessTokenRequest(request, new DateInterval("1h"));

      // assert
      await expect(accessTokenResponse).rejects.toThrowError(
        /Code verifier must follow the specifications of RFS-7636/,
      );
    });

    it("throws for incorrect code_verifier", async () => {
      // act
      request = new OAuthRequest({
        body: {
          grant_type: "authorization_code",
          code: authorizationCode,
          redirect_uri: authorizationRequest.redirectUri,
          client_id: client.id,
          code_verifier: codeVerifier + "broken",
        },
      });
      const accessTokenResponse = grant.respondToAccessTokenRequest(request, new DateInterval("1h"));

      // assert
      await expect(accessTokenResponse).rejects.toThrowError(/Failed to verify code challenge/);
    });
  });
});
Example #9
Source File: implicit.grant.spec.ts    From ts-oauth2-server with MIT License 4 votes vote down vote up
describe("implicit grant", () => {
  let user: OAuthUser;
  let client: OAuthClient;
  let scope1: OAuthScope;
  let scope2: OAuthScope;

  let grant: ImplicitGrant;

  let request: OAuthRequest;

  beforeEach(() => {
    request = new OAuthRequest();

    user = {
      id: "512ab9a4-c786-48a6-8ad6-94c53a8dc651",
      password: "password123",
    };
    client = {
      id: "35615f2f-13fa-4731-83a1-9e34556ab390",
      name: "test client",
      secret: "super-secret-secret",
      redirectUris: ["http://example.com"],
      allowedGrants: ["implicit"],
      scopes: [],
    };
    scope1 = { name: "scope-1" };
    scope2 = { name: "scope-2" };

    grant = new ImplicitGrant(
      inMemoryAuthCodeRepository,
      inMemoryClientRepository,
      inMemoryAccessTokenRepository,
      inMemoryScopeRepository,
      inMemoryUserRepository,
      new JwtService("secret-key"),
    );

    inMemoryDatabase.clients[client.id] = client;
    inMemoryDatabase.users[user.id] = user;
    inMemoryDatabase.scopes[scope1.name] = scope1;
    inMemoryDatabase.scopes[scope2.name] = scope2;
  });

  describe("validate authorization request", () => {
    it("is successful with minimal request", async () => {
      // arrange
      request = new OAuthRequest({
        query: {
          response_type: "token",
          client_id: client.id,
          redirect_uri: "http://example.com",
        },
      });

      // act
      const authorizationRequest = await grant.validateAuthorizationRequest(request);

      // assert
      expect(authorizationRequest.isAuthorizationApproved).toBe(false);
      expect(authorizationRequest.client.id).toBe(client.id);
      expect(authorizationRequest.client.name).toBe(client.name);
      expect(authorizationRequest.redirectUri).toBe("http://example.com");
      expect(authorizationRequest.state).toBeUndefined();
      expect(authorizationRequest.codeChallenge).toBeUndefined();
      expect(authorizationRequest.codeChallengeMethod).toBeUndefined();
      expect(authorizationRequest.scopes).toStrictEqual([]);
    });

    it("is successful with state and scopes", async () => {
      // arrange
      request = new OAuthRequest({
        query: {
          response_type: "token",
          client_id: client.id,
          redirect_uri: "http://example.com",
          state: "f2ae4dc5-b535-4949-aaed-54ebbf08e876",
          scope: "scope-1 scope-2",
        },
      });

      // act
      const authorizationRequest = await grant.validateAuthorizationRequest(request);

      // assert
      expect(authorizationRequest.isAuthorizationApproved).toBe(false);
      expect(authorizationRequest.client.id).toBe(client.id);
      expect(authorizationRequest.client.name).toBe(client.name);
      expect(authorizationRequest.redirectUri).toBe("http://example.com");
      expect(authorizationRequest.state).toBe("f2ae4dc5-b535-4949-aaed-54ebbf08e876");
      expect(authorizationRequest.codeChallenge).toBeUndefined();
      expect(authorizationRequest.codeChallengeMethod).toBeUndefined();
      expect(authorizationRequest.scopes).toStrictEqual([scope1, scope2]);
    });

    it("throws if missing client_id", async () => {
      // arrange
      request = new OAuthRequest({
        query: {
          response_type: "token",
        },
      });

      // act
      const authorizationRequest = grant.validateAuthorizationRequest(request);

      // assert
      await expect(authorizationRequest).rejects.toThrowError(/Check the `client_id` parameter/);
    });

    it("throws if missing redirect_uri", async () => {
      // arrange
      request = new OAuthRequest({
        query: {
          response_type: "token",
          client_id: client.id,
        },
      });

      // act
      const authorizationRequest = await grant.validateAuthorizationRequest(request);

      // assert
      expect(authorizationRequest.redirectUri).toBe("http://example.com");
    });

    it("throws when passed invalid scopes", async () => {
      // arrange
      request = new OAuthRequest({
        query: {
          response_type: "token",
          client_id: client.id,
          redirect_uri: "http://example.com",
          scope: "scope-1 non-existant non-existant-2",
        },
      });

      // act
      const authorizationRequest = grant.validateAuthorizationRequest(request);

      // assert
      await expect(authorizationRequest).rejects.toThrowError(/Check the `non-existant, non-existant-2` scope\(s\)/);
    });
  });

  describe("complete authorization request", () => {
    it("is successful", async () => {
      // arrange
      const now = roundToSeconds(Date.now());
      const authorizationRequest = new AuthorizationRequest("implicit", client, "http://example.com");
      authorizationRequest.user = user;
      authorizationRequest.isAuthorizationApproved = true;
      authorizationRequest.state = "abc123-state";

      // act
      const response = await grant.completeAuthorizationRequest(authorizationRequest);
      const authorizeResponseQuery = querystring.parse(response.headers.location.split("?")[1]);
      const decodedCode = <ITokenData>decode(String(authorizeResponseQuery.access_token));

      // assert
      expect(authorizeResponseQuery.state).toBe("abc123-state");
      expect(decodedCode.sub).toBe(user.id);
      expect(decodedCode.jti).toMatch(REGEX_ACCESS_TOKEN);
      expect(decodedCode.exp).toBeGreaterThan(now);
      expect(decodedCode.iat).toBe(now);
    });

    it("will not complete if isAuthorizationApproved=false", async () => {
      // arrange
      const authorizationRequest = new AuthorizationRequest("implicit", client, "http://example.com");
      authorizationRequest.user = user;
      authorizationRequest.isAuthorizationApproved = false;

      // act
      const response = grant.completeAuthorizationRequest(authorizationRequest);

      //assert
      await expect(response).rejects.toThrowError(/The resource owner or authorization server denied the request/);
    });

    // it("uses clients redirect url if request ", async () => {});
  });

  describe("canRespondToAccessTokenRequest", () => {
    it("valid request can respond", async () => {
      // arrange
      request = new OAuthRequest({
        query: {
          response_type: "token",
          client_id: client.id,
        },
      });

      // act
      const canRespond = grant.canRespondToAuthorizationRequest(request);

      // assert
      expect(canRespond).toBeTruthy();
    });

    it("invalid request cannot respond", async () => {
      // arrange
      request = new OAuthRequest({ query: {} });

      // assert
      expect(grant.canRespondToAuthorizationRequest(request)).toBeFalsy();
      expect(grant.canRespondToAccessTokenRequest(request)).toBeFalsy();
    });
  });

  describe("respondToAccessTokenRequest", () => {
    it("throws because implicit grant cannot respond to access token requests", async () => {
      // assert
      expect(() => grant.respondToAccessTokenRequest(request, new DateInterval("1h"))).toThrowError(
        /The implicit grant can't respond to access token requests/,
      );
    });
  });
});
Example #10
Source File: sep10.ts    From stellar-anchor-tests with Apache License 2.0 4 votes vote down vote up
export async function postChallenge(
  clientKeypair: Keypair,
  webAuthEndpoint: string,
  networkPassphrase: string,
  result: Result,
  useJson: boolean = false,
  challenge?: Transaction,
): Promise<string | void> {
  if (!challenge) {
    challenge = (await getChallenge(
      clientKeypair,
      webAuthEndpoint,
      networkPassphrase,
      result,
    )) as Transaction;
    if (!challenge) return;
    challenge.sign(clientKeypair);
  }
  let request: Request;
  if (useJson) {
    request = new Request(webAuthEndpoint, {
      method: "POST",
      body: JSON.stringify({ transaction: challenge.toXDR() }),
      headers: { "Content-Type": "application/json" },
    });
  } else {
    request = new Request(webAuthEndpoint, {
      method: "POST",
      body: `transaction=${encodeURIComponent(challenge.toXDR())}`,
      headers: { "Content-Type": "application/x-www-form-urlencoded" },
    });
  }
  const postAuthCall: NetworkCall = { request: request };
  result.networkCalls.push(postAuthCall);
  try {
    postAuthCall.response = await fetch(postAuthCall.request.clone());
  } catch {
    result.failure = makeFailure(postChallengeFailureModes.CONNECTION_ERROR, {
      url: postAuthCall.request.url,
    });
    return;
  }
  if (postAuthCall.response.status !== 200) {
    result.failure = makeFailure(
      postChallengeFailureModes.UNEXPECTED_STATUS_CODE,
    );
    result.expected = 200;
    result.actual = postAuthCall.response.status;
    return;
  }
  const postAuthResponseContentType =
    postAuthCall.response.headers.get("Content-Type");
  if (
    !postAuthResponseContentType ||
    !postAuthResponseContentType.includes("application/json")
  ) {
    result.failure = makeFailure(postChallengeFailureModes.BAD_CONTENT_TYPE);
    result.expected = "application/json";
    if (postAuthResponseContentType)
      result.actual = postAuthResponseContentType;
    return;
  }
  const responseBody = await postAuthCall.response.clone().json();
  if (!responseBody.token) {
    result.failure = makeFailure(postChallengeFailureModes.NO_TOKEN);
    return;
  }
  let jwtContents;
  try {
    jwtContents = decode(responseBody.token);
  } catch (e) {
    result.failure = makeFailure(postChallengeFailureModes.JWT_DECODE_FAILURE, {
      error: e.message,
    });
    return;
  }
  if (!jwtContents || typeof jwtContents !== "object") {
    result.failure = makeFailure(postChallengeFailureModes.JWT_NOT_JSON);
    return;
  }
  const validatorResult = validate(jwtContents, jwtSchema);
  if (validatorResult.errors.length !== 0) {
    result.failure = makeFailure(postChallengeFailureModes.INVALID_JWT_SCHEMA, {
      errors: validatorResult.errors.join("\n"),
    });
    return;
  }
  try {
    Keypair.fromPublicKey(jwtContents.sub);
  } catch {
    result.failure = makeFailure(postChallengeFailureModes.INVALID_JWT_SUB);
    return;
  }
  return responseBody.token;
}